(并发)线程的创建
来源:互联网 发布:水中刀 知乎 编辑:程序博客网 时间:2024/06/18 15:23
定义任务
要想定义任务,只需要实现Runnable接口。
public class LiftOff implements Runnable{ protected int countDown = 10; private static int taskCount = 0; private final int id = taskCount++; public LiftOff(){} public LiftOff(int countDown){ this.countDown = countDown; } public String status(){ return "#" + id + "(" + (countDown > 0 ? countDown : "liftoff!") + "),"; } public void run() { while(countDown-- > 0){ System.out.print(status()); Thread.yield(); } } public static void main(String[] args) { new LiftOff().run(); }}
标识符id可以用来去人任务的多个实例,它是final的,因为它在被创建的时候就不希望被修改。在run()方法中,总是会以某种形式的循环来进行,使得任务一直运行下去知道不再需要。通常run()是会被写成无限循环的,因此我们就不得不考虑如何安全的终止线程。yield()是对线程的调度器。它声明:我已执行完声明周期中最重要的部分,可以切换给其他任务。当然这完全是选择性了,如果开启几个任务,我们可能会看见任务在切换进行。
Thread类
将Runnable对象转变为工作任务的传统方式是把它提交给Thread构造器。
public class BasicThreads { public static void main(String[] args) { Thread t = new Thread(new LiftOff()); t.start(); System.out.println("Waiting for LiftOff"); }}//outputWaiting for LiftOff#0(9),#0(8),#0(7),#0(6),#0(5),#0(4),#0(3),#0(2),#0(1),#0(liftoff!),
Thread构造器只需要一个Runnable对象。调用Thread对象的start()方法为该线程执行必须的初始化操作,然后调用Runnable的run()方法,以便在这个新线程中启动该任务。可以看到Waiting for LiftOff在LiftOff.run()之前就已经输出,则表示该句输出是main()线程和LiftOff.run()在执行。这并不表示只有main()线程才能这样,任何线程都能启动另一个线程。接下来我们看看更多任务被更多的线程驱动:
public class MoreBasicThreads { public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(new LiftOff()).start(); } System.out.println("Waiting for LiftOff"); }}//out#0(9),#0(8),#2(9),#1(9),#4(9),#2(8),#2(7),#2(6),#0(7),Waiting for LiftOff#3(9),#0(6),#2(5),#4(8),#1(8),#2(4),#4(7),#1(7),#1(6),#0(5),#3(8),#3(7),#3(6),#3(5),#3(4),#3(3),#3(2),#0(4),#1(5),#1(4),#4(6),#2(3),#4(5),#1(3),#4(4),#0(3),#3(1),#0(2),#0(1),#0(liftoff!),#4(3),#4(2),#4(1),#4(liftoff!),#1(2),#1(1),#1(liftoff!),#2(2),#2(1),#2(liftoff!),#3(liftoff!),
从输出可以看出不同的任务的执行在线程被换进换出时混在一起了。这种交换是线程调度器自行控制的。在使用普通对象时对GC来说是公平的,但是在使用Thread时,情况就不同了。在其每个Thread任务退出其run()没有死亡之前,GC无法清楚它。可以看出这些任务确定都是执行了整个流程,因此,**一个线程会创建一个单独的执行线程,在对start()调用完成之后,它仍旧会继续存在。**
使用Executor
Java SE5的java.util.concurrent包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程。Executor在客户端和任务执行端提供了一个间接层;与客户端直接执行任务不同,这个终结对象将执行任务。Executor允许你管理异步任务的执行,而无须显式地管理线程的声明周期。
public class CacheThreadPool { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { exec.execute(new LiftOff()); } exec.shutdown(); }}//out#1(9),#0(9),#4(9),#2(9),#2(8),#2(7),#2(6),#3(9),#2(5),#3(8),#4(8),#0(8),#1(8),#0(7),#1(7),#4(7),#1(6),#1(5),#3(7),#3(6),#2(4),#3(5),#1(4),#4(6),#0(6),#4(5),#1(3),#3(4),#2(3),#3(3),#3(2),#1(2),#1(1),#4(4),#0(5),#0(4),#0(3),#4(3),#4(2),#1(liftoff!),#3(1),#3(liftoff!),#2(2),#2(1),#2(liftoff!),#4(1),#4(liftoff!),#0(2),#0(1),#0(liftoff!),
ExecutorService是具有服务周期的Exrcutor,例如关闭。它知道如何构建恰当的上下文来执行Runnable对象。ExecutorSerivce对象使用的是静态的Executor方法创建的,这个方法可以确定其Executor类型;非常常见的情况是,单个Executor被用来创建和管理系统中所有的任务。对shutdown()方法的调用可以防止新任务被提交给这个Executor,当前线程(在上述例子就是main()的线程)将继续运行在shutdown()被调用之前提交的所有任务。也就是这个main()只会执行上面循环中的任务。在执行完成就会尽快退出。
你可以很容易将示例中的CachedThreadPool替换为其他类型。
public static void main(String[] args) { ExecutorService exec = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { exec.execute(new LiftOff()); } exec.shutdown(); }//out#4(4),#1(4),#4(3),#0(4),#2(4),#3(4),#2(3),#0(3),#4(2),#1(3),#4(1),#0(2),#2(2),#3(3),#2(1),#0(1),#4(liftoff!),#1(2),#0(liftoff!),#2(liftoff!),#3(2),#1(1),#3(1),#1(liftoff!),#3(liftoff!),//当启动线程是1的情况#0(4),#0(3),#0(2),#0(1),#0(liftoff!),#1(4),#1(3),#1(2),#1(1),#1(liftoff!),#2(4),#2(3),#2(2),#2(1),#2(liftoff!),#3(4),#3(3),#3(2),#3(1),#3(liftoff!),#4(4),#4(3),#4(2),#4(1),#4(liftoff!),#5(4),#5(3),#5(2),#5(1),#5(liftoff!),
FixedThreadPool固定线程数量。它的好处就是,可以节省创建线程的开销,因为它是一次性的预先执行代价高昂的线程分配。
Executor类型说明
CachedThreadPool是我们会主要使用的类型,它是在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是合理的Executor的首选。只有在这种方式会引发问题时,你才需要切换到FixedThreadPool。SingleThreadExecutor就像是线程数量为1的FixedThreadPool。这对于希望某个任务长期连续的运行很有用。如果对SingleThreadExecutor提交了多个任务,那么这些任务将会排队执行。因此,SingleThreadExecutor会序列化所有提交给它的任务,并会维护它自己的悬挂任务队列。
从任务中产生返回值
public class TaskWithResult implements Callable<String>{ private int id; public TaskWithResult(int id){ this.id = id; } public String call() throws Exception { return "result of TackWithResult " + id; }}public class CallableDemo{ public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); ArrayList<Future<String>> results = new ArrayList<>(); for (int i = 0; i < 10; i++) { results.add(exec.submit(new TaskWithResult(i))); } for (Future<String> future : results) { try { System.out.println(future.get()); } catch (InterruptedException e) { System.out.println(e); return; } catch (ExecutionException e) { System.out.println(e); } finally{ exec.shutdown(); } } }}//outresult of TackWithResult 0result of TackWithResult 1result of TackWithResult 2result of TackWithResult 3result of TackWithResult 4result of TackWithResult 5result of TackWithResult 6result of TackWithResult 7result of TackWithResult 8result of TackWithResult 9
submit()方法会产生Future对象,它用Callable返回结果的特定类型进行了参数化。你可以用isDone()方法来查询Future是否已经完成。
阅读全文
1 0
- (并发)线程的创建
- Java并发编程:(1)进程和线程的由来、进程的创建、线程的创建
- 线程并发库(创建线程、守护线程、暂停线程)
- Java并发总结(一): 线程的介绍及创建
- Java并发编程:线程、进程的创建
- Java并发线程--多线程的创建
- java并发多线程,线程的创建启动
- 【Java并发学习】之线程的创建
- JAVA 并发编程-线程创建(二)
- 并发编程(3)java创建线程
- JAVA 并发编程-线程创建(二)
- JAVA 并发编程-线程创建(二)
- ios创建并发线程
- Java并发编程——线程池的使用(一) 简单创建线程池
- 并发实战值之----线程池--线程池的创建
- Java并发编程之线程管理(线程创建1)
- Java Executor并发框架(四)创建线程池的核心参数的解释
- Java并发编程:线程创建
- 基于OnPaint的Slider进度条自绘实现
- windows命令行下的常见命令
- C++:标准模板库map
- 最牛语义匹配算法_速围观!
- 【HDU-1722】 Cake
- (并发)线程的创建
- TEX Quotes
- go语言即将才能一统江湖_你还不来学?
- XYOJ 三个数排序输出
- 【hdu 1722】 Cake
- Eclipse 导出设置
- 存储过程入门
- sphinx 字符串过滤
- 关于c++类的赋值的问题