java多线程(三)-Executors实现的几种线程池以及Callable

从java5开始,类库中引入了很多新的管理调度线程的API,最常用的就是Executor(执行器)框架。Executor帮助程序员管理Thread对象,简化了并发编程,它其实就是在 提供了一个中间层,方便程序员管理异步任务的执行,而又不用显式的管理线程的生命周期。


Executor采用了线程池实现,也更节约开销,因为是我们启动新线程的首选方法。

 

示例代码:src/thread_runnable/CachedThreadPool.java

 1 public class CachedThreadPool{
 2     
 3     public static void main(String[] args) {
 4         ExecutorService exec = Executors.newCachedThreadPool();
 5         
 6         
 7         System.out.println("main start  "  + Thread.currentThread().getName());
 8         for (int i=0; i<3; i++){
 9             exec.execute(new CountDown());
10         }
11         
12         exec.shutdown();
13         System.out.println("main end  "  + Thread.currentThread().getName());
14     }
15 
16 }

输出结果:(每次执行输出结果都有差异)

 

 

Executors类通过提供一系列的工厂方法来创建线程池,返回的线程池都实现了ExecutorService接口,ExecutorService接口实现了Executor接口,提供了更丰富的方法来管理线程池,ExecutorService对象可以通过execute(Runnable)/submit(Callable)方法来开始执行新的方法(这本身也属于命令设计模式)。

ExecutorService(其实就是线程池)的生命周期包括三种状态,运行,关闭,终止。创建后便进入运行状态。调用shutdown()方法就进入关闭状态。此时ExecutorService不再接受新的任务,但是它还在执行已经提交的任务,等到所有的任务都执行完毕后,就到了终止状态。


Executors类通过提供一系列的工厂方法来创建线程池,常见类型如下:

1 public static ExecutorService newFixedThreadPool(int nThreads)

创建固定数目的线程池

 

 

1 public static ExecutorService newCachedThreadPool()

创建一个可缓存的线程池,调用execute添加新任务后,如果有之前构造的可用线程,则重用该线程;否则,就创建一个新线程并添加到池中。并且可以从缓存中移除那些60s未被使用的线程。

 

1 public static ExecutorService newSingleThreadExecutor()

创建一个单线程的线程池。其实只有一个线程,所有的任务按照指定的顺序,(FIFO,LIFO,优先级)来执行。

 

1 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个定长线程池,支持定时及周期性的任务执行,所以很多时候可用来代替Timer类。

 

 

看一个简单的例子,代码地址:src/thread_runnable/FixedThreadPool.java

 1 public class FixedThreadPool{
 2     public static void main(String[] args) {
 3         int ThreadCount = 2;    
 4         ExecutorService exec = Executors.newFixedThreadPool(ThreadCount);
 5         
 6         System.out.println("main start  "  + Thread.currentThread().getName());
 7         for (int i=0; i<3; i++){
 8             exec.execute(new CountDown());
 9         }
10         exec.shutdown();
11         System.out.println("main end  "  + Thread.currentThread().getName());
12         
13     }
14 }

当ThreadCount = 2时,某一次输出结果为:

 

从输出结果可以看出,在最初的时候,只有thread-1和thread-2两个线程在执行任务,当之前的任务执行完成之后,thread-2被复用,开始执行第三个任务。
那么我们可以想象,如果我们把 让ThreadCount = 1,那么应该只存在一个线程,其执行完一个任务,再接着执行另一个。
修改ThreadCount = 1,输出结果符合我们的预期:

 

可以周期或者delay执行的 线程也是在实际项目中也是比较常见的。
示例代码地址:src\thread_runnable\ScheduledExecutorServiceDemo.java

 1 public class ScheduledExecutorServiceDemo {
 2     public static void main(String[] args) {
 3         // TODO Auto-generated method stub
 4         ScheduledExecutorService mScheduledService  = Executors.newScheduledThreadPool(2);
 5         System.out.println("ScheduledExecutorServiceDemo --- main start, " + Thread.currentThread().getName());
 6         mScheduledService.schedule(new CountDown(), 2 , TimeUnit.SECONDS);
 7         System.out.println("ScheduledExecutorServiceDemo --- main end, " + Thread.currentThread().getName());
 8         mScheduledService.shutdown();
 9     }
10 }

输出结果:(通过实际输出,可以看到 ,两秒钟之后,才开始执行任务当中的打印 语句)

 

关于周期性任务的demo。
示例代码地址:src\thread_runnable\ScheduledFixedServiceDemo_2.java

 1 public class ScheduledFixedServiceDemo_2 {
 2     public static void main(String[] args) {
 3         // TODO Auto-generated method stub
 4         ScheduledExecutorService mScheduledService  = Executors.newScheduledThreadPool(2);
 5         System.out.println("ScheduledFixedServiceDemo_2 --- main start, " + Thread.currentThread().getName());
 6         mScheduledService.scheduleWithFixedDelay(new Runnable() {
 7             int count = 0;
 8             @Override
 9             public void run() {
10                 System.out.println("fixed print = " + count++);
11                 
12             }
13         }, 1, 2 , TimeUnit.SECONDS);
14         System.out.println("ScheduledFixedServiceDemo_2 --- main end, " + Thread.currentThread().getName());
15     }
16 }

输出结果:

…..后面每间隔2s,输出一次。

 

 

 

Runnable虽然执行任务,但是没有返回值,而java 5之后 ,重新引入了一个 Callable接口,它是一个具有类型参数的泛型,表示从call()方法中返回的值。不过需要注意的是,把Callable对象传递给ExecutorService的submit方法后,在新线程上执行的call()方法返回的其实是 Future对象,Future对象有isDone(),get()等方法来判断是否完成,以及得到返回值等。

代码地址:src\thread_runnable\CallableDemo.java

 1 class TaskWithResult implements Callable<String> {
 2     public String call() throws Exception {        
 3         return "result of TaskWithResult from Callable "  + "\t " + Thread.currentThread().getName();
 4     }
 5 }
 6 
 7 
 8 public class CallableDemo {
 9     public static void main(String[] args) {
10         // TODO Auto-generated method stub
11         ExecutorService exec = Executors.newCachedThreadPool();
12         
13         System.out.println("main  start\t" + Thread.currentThread().getName());
14         Future<String> fs = exec.submit(new TaskWithResult());
15         exec.shutdown();
16         
17         try {
18             //当callable对象并没有完成时,这里一直是堵塞的。
19             System.out.println(fs.get());
20         } catch (Exception e) {
21             e.printStackTrace();
22         }
23         System.out.println("main  end\t" + Thread.currentThread().getName());
24     }    
25 }

 


输出结果:

 

 

这几篇java多线程文章的demo代码下载地址 http://download.csdn.net/detail/yaowen369/9786452

 

-------
作者: www.yaoxiaowen.com
github: https://github.com/yaowen369

posted @ 2017-03-18 23:49  eleven_yw  阅读(1544)  评论(0编辑  收藏  举报