教大家利用ExecutorService对线程进行操作

ExecutorService 建立多线程的步骤:


1、定义线程类
class Handler implements Runnable{
}
2、建立ExecutorService线程池
ExecutorService executorService = Executors.newCachedThreadPool();

或者
int cpuNums = Runtime.getRuntime().availableProcessors();
                //获取当前系统的CPU 数目
ExecutorService executorService =Executors.newFixedThreadPool(cpuNums * POOL_SIZE);
                //ExecutorService通常根据系统资源情况灵活定义线程池大小
3、调用线程池操作
循环操作,成为daemon,把新实例放入Executor池中
      while(true){
        executorService.execute(new Handler(socket)); 
           // class Handler implements Runnable{
        或者
        executorService.execute(createTask(i));
            //private static Runnable createTask(final int taskID)
      }

execute(Runnable对象)方法
其实就是对Runnable对象调用start()方法
(当然还有一些其他后台动作,比如队列,优先级,IDLE timeout,active激活等)


几种不同的ExecutorService线程池对象
1.newCachedThreadPool() 
-缓存型池子,先查看池中有没有以前建立的线程,如果有,就reuse.如果没有,就建一个新的线程加入池中
-缓存型池子通常用于执行一些生存期很短的异步型任务
 因此在一些面向连接的daemon型SERVER中用得不多。
-能reuse的线程,必须是timeout IDLE内的池中线程,缺省timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
  注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。
2. newFixedThreadPool
-newFixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程
-其独特之处:任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子
-和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的),所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器
-从方法的源代码看,cache池和fixed 池调用的是同一个底层池,只不过参数不同:
fixed池线程数固定,并且是0秒IDLE(无IDLE)
cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE  
3.ScheduledThreadPool
-调度型线程池
-这个池子里的线程可以按schedule依次delay执行,或周期执行
4.SingleThreadExecutor
-单例线程,任意时间池中只能有一个线程
-用的是和cache池和fixed池相同的底层池,但线程数目是1-1,0秒IDLE(无IDLE)


上面四种线程池,都使用Executor的缺省线程工厂建立线程,也可单独定义自己的线程工厂
下面是缺省线程工厂代码:

    
[java]  view plain copy print ?
  1. static class DefaultThreadFactory implements ThreadFactory {  
  2.         static final AtomicInteger poolNumber = new AtomicInteger(1);  
  3.         final ThreadGroup group;  
  4.         final AtomicInteger threadNumber = new AtomicInteger(1);  
  5.         final String namePrefix;  
  6.   
  7.   
  8.         DefaultThreadFactory() {  
  9.             SecurityManager s = System.getSecurityManager();  
  10.             group = (s != null)? s.getThreadGroup() :Thread.currentThread().getThreadGroup();  
  11.             
  12.             namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";  
  13.         }  
  14.   
  15.   
  16.         public Thread newThread(Runnable r) {  
  17.             Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0);  
  18.             if (t.isDaemon())  
  19.                 t.setDaemon(false);  
  20.             if (t.getPriority() != Thread.NORM_PRIORITY)  
  21.                 t.setPriority(Thread.NORM_PRIORITY);  
  22.             return t;  
  23.         }  
  24.     }  


也可自己定义ThreadFactory,加入建立池的参数中
 public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {

Executor的execute()方法
execute() 方法将Runnable实例加入pool中,并进行一些pool size计算和优先级处理
execute() 方法本身在Executor接口中定义,有多个实现类都定义了不同的execute()方法
如ThreadPoolExecutor类(cache,fiexed,single三种池子都是调用它)的execute方法如下:

    
[java]  view plain copy print ?
  1. public void execute(Runnable command) {  
  2.         if (command == null)  
  3.             throw new NullPointerException();  
  4.         if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {  
  5.             if (runState == RUNNING && workQueue.offer(command)) {  
  6.                 if (runState != RUNNING || poolSize == 0)  
  7.                     ensureQueuedTaskHandled(command);  
  8.             }  
  9.             else if (!addIfUnderMaximumPoolSize(command))  
  10.                 reject(command); // is shutdown or saturated  
  11.         }  
  12.     }  






在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动、调度、管理线程的一大堆API了。在Java5以后,通过 Executor来启动线程比用Thread的start()更好。在新特征中,可以很容易控制线程的启动、执行和关闭过程,还可以很容易使用线程池的特 性。
 
一、创建任务
 
任务就是一个实现了Runnable接口的类。
创建的时候实run方法即可。
 
二、执行任务
 
通过java.util.concurrent.ExecutorService接口对象来执行任务,该接口对象通过工具类java.util.concurrent.Executors的静态方法来创建。
 
Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
 
ExecutorService提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。 可以关闭 ExecutorService,这将导致其停止接受新任务。关闭后,执行程序将最后终止,这时没有任务在执行,也没有任务在等待执行,并且无法提交新任 务。
            executorService.execute(new TestRunnable());
 
1、创建ExecutorService
通过工具类java.util.concurrent.Executors的静态方法来创建。
Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
 
比如,创建一个ExecutorService的实例,ExecutorService实际上是一个线程池的管理工具:
        ExecutorService executorService = Executors.newCachedThreadPool();
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        ExecutorService executorService = Executors.newSingleThreadExecutor();
 
2、将任务添加到线程去执行
当将一个任务添加到线程池中的时候,线程池会为每个任务创建一个线程,该线程会在之后的某个时刻自动执行。
 
三、关闭执行服务对象
        executorService.shutdown();
 
四、综合实例

 
[java]  view plain copy print ?
  1. package concurrent;   
  2.   
  3.   
  4. import java.util.concurrent.ExecutorService;   
  5. import java.util.concurrent.Executors;   
  6.   
  7.   
  8. /**  
  9. * Created by IntelliJ IDEA.  
  10.  
  11. * @author leizhimin 2008-11-25 14:28:59  
  12. */  
  13. publicclass TestCachedThreadPool {   
  14.         publicstaticvoid main(String[] args) {   
  15. //                ExecutorService executorService = Executors.newCachedThreadPool();  
  16.                 ExecutorService executorService = Executors.newFixedThreadPool(5);  
  17. //         ExecutorService executorService = Executors.newSingleThreadExecutor();  
  18.   
  19.   
  20.                 for (int i = 0; i < 5; i++) {   
  21.                         executorService.execute(new TestRunnable());   
  22.                         System.out.println("************* a" + i + " *************");   
  23.                 }   
  24.                 executorService.shutdown();   
  25.         }   
  26. }   
  27.   
  28.   
  29. class TestRunnable implements Runnable {   
  30.         publicvoid run() {   
  31.                 System.out.println(Thread.currentThread().getName() + "线程被调用了。");   
  32.                 while (true) {   
  33.                         try {   
  34.                                 Thread.sleep(5000);   
  35.                                 System.out.println(Thread.currentThread().getName());   
  36.                         } catch (InterruptedException e) {   
  37.                                 e.printStackTrace();   
  38.                         }   
  39.                 }   
  40.         }   
  41. }  


 
运行结果:
************* a0 ************* 
************* a1 ************* 
pool-1-thread-2线程被调用了。 
************* a2 ************* 
pool-1-thread-3线程被调用了。 
pool-1-thread-1线程被调用了。 
************* a3 ************* 
************* a4 ************* 
pool-1-thread-4线程被调用了。 
pool-1-thread-5线程被调用了。 
pool-1-thread-2 
pool-1-thread-1 
pool-1-thread-3 
pool-1-thread-5 
pool-1-thread-4 
pool-1-thread-2 
pool-1-thread-1 
pool-1-thread-3 
pool-1-thread-5 
pool-1-thread-4 
     ......
 
 
五、获取任务的执行的返回值
在Java5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被 ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只 能通过ExecutorService的(<T> task) 方法来执行,并且返回一个 <T><T>,是表示任务等待完成的 Future。
 
public interface Callable<V>
返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。
Callable 接口类似于,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
类包含一些从其他普通形式转换成 Callable 类的实用方法。
 
 
Callable中的call()方法类似Runnable的run()方法,就是前者有返回值,后者没有。
 
当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。
 
同样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null。
 
遗憾的是,在Java API文档中,这块介绍的很糊涂,估计是翻译人员还没搞清楚的缘故吧。或者说是注释不到位。下面看个例子:

 
[java]  view plain copy print ?
  1. import java.util.ArrayList;   
  2. import java.util.List;   
  3. import java.util.concurrent.*;   
  4.   
  5.   
  6. /**  
  7. * Callable接口测试  
  8.  
  9. * @author leizhimin 2008-11-26 9:20:13  
  10. */  
  11. publicclass CallableDemo {   
  12.         publicstaticvoid main(String[] args) {   
  13.                 ExecutorService executorService = Executors.newCachedThreadPool();   
  14.                 List<Future<String>> resultList = new ArrayList<Future<String>>();   
  15.   
  16.   
  17.                 //创建10个任务并执行  
  18.                 for (int i = 0; i < 10; i++) {   
  19.                         //使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中  
  20.                         Future<String> future = executorService.submit(new TaskWithResult(i));   
  21.                         //将任务执行结果存储到List中  
  22.                         resultList.add(future);   
  23.                 }   
  24.   
  25.   
  26.                 //遍历任务的结果  
  27.                 for (Future<String> fs : resultList) {   
  28.                         try {   
  29.                                 System.out.println(fs.get());     //打印各个线程(任务)执行的结果  
  30.                         } catch (InterruptedException e) {   
  31.                                 e.printStackTrace();   
  32.                         } catch (ExecutionException e) {   
  33.                                 e.printStackTrace();   
  34.                         } finally {   
  35.                                 //启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用。  
  36.                                 executorService.shutdown();   
  37.                         }   
  38.                 }   
  39.         }   
  40. }   
  41.   
  42.   
  43.   
  44.   
  45. class TaskWithResult implements Callable<String> {   
  46.         privateint id;   
  47.   
  48.   
  49.         public TaskWithResult(int id) {   
  50.                 this.id = id;   
  51.         }   
  52.   
  53.   
  54.         /**  
  55.          * 任务的具体过程,一旦任务传给ExecutorService的submit方法,则该方法自动在一个线程上执行。  
  56.          *  
  57.          * @return  
  58.          * @throws Exception  
  59.          */  
  60.         public String call() throws Exception {   
  61.                 System.out.println("call()方法被自动调用,干活!!!             " + Thread.currentThread().getName());   
  62.                 //一个模拟耗时的操作  
  63.                 for (int i = 999999; i > 0; i--) ;   
  64.                 return"call()方法被自动调用,任务的结果是:" + id + "    " + Thread.currentThread().getName();   
  65.         }   
  66. }  


 
运行结果:
call()方法被自动调用,干活!!!             pool-1-thread-1 
call()方法被自动调用,干活!!!             pool-1-thread-3 
call()方法被自动调用,干活!!!             pool-1-thread-4 
call()方法被自动调用,干活!!!             pool-1-thread-6 
call()方法被自动调用,干活!!!             pool-1-thread-2 
call()方法被自动调用,干活!!!             pool-1-thread-5 
call()方法被自动调用,任务的结果是:0    pool-1-thread-1 
call()方法被自动调用,任务的结果是:1    pool-1-thread-2 
call()方法被自动调用,干活!!!             pool-1-thread-2 
call()方法被自动调用,干活!!!             pool-1-thread-6 
call()方法被自动调用,干活!!!             pool-1-thread-4 
call()方法被自动调用,任务的结果是:2    pool-1-thread-3 
call()方法被自动调用,干活!!!             pool-1-thread-3 
call()方法被自动调用,任务的结果是:3    pool-1-thread-4 
call()方法被自动调用,任务的结果是:4    pool-1-thread-5 
call()方法被自动调用,任务的结果是:5    pool-1-thread-6 
call()方法被自动调用,任务的结果是:6    pool-1-thread-2 
call()方法被自动调用,任务的结果是:7    pool-1-thread-6 
call()方法被自动调用,任务的结果是:8    pool-1-thread-4 
call()方法被自动调用,任务的结果是:9    pool-1-thread-3 

  ExecutorService的execute和submit方法三个区别:

1、接收的参数不一样

2、submit有返回值,而execute没有

Method submit extends base method Executor.execute by creating and returning a Future that can be used to cancel execution and/or wait for completion. 

用到返回值的例子,比如说我有很多个做validation的task,我希望所有的task执行完,然后每个task告诉我它的执行结果,是成功还是失败,如果是失败,原因是什么。然后我就可以把所有失败的原因综合起来发给调用者。


个人觉得cancel execution这个用处不大,很少有需要去取消执行的。


而最大的用处应该是第二点。


3、submit方便Exception处理


There is a difference when looking at exception handling. If your tasks throws an exception and if it was submitted with execute this exception will go to the uncaught exception handler (when you don't have provided one explicitly, the default one will just print the stack trace to System.err). If you submitted the task with submit any thrown exception, checked or not, is then part of the task's return status. For a task that was submitted with submit and that terminates with an exception, the Future.get will rethrow this exception, wrapped in an ExecutionException.
意思就是如果你在你的task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。



比如说,我有很多更新各种数据的task,我希望如果其中一个task失败,其它的task就不需要执行了。那我就需要catch Future.get抛出的异常,然后终止其它task的执行,代码如下:

51cto上有一篇非常好的文章“Java5并发学习”(http://lavasoft.blog.51cto.com/62575/115112),下面的代码是基于它之上修改的。


[java]  view plain copy print ?
  1. import java.util.ArrayList;    
  2. import java.util.List;    
  3. import java.util.Random;    
  4. import java.util.concurrent.Callable;    
  5. import java.util.concurrent.ExecutionException;    
  6. import java.util.concurrent.ExecutorService;    
  7. import java.util.concurrent.Executors;    
  8. import java.util.concurrent.Future;    
  9.     
  10. public class ExecutorServiceTest {    
  11.     public static void main(String[] args) {    
  12.         ExecutorService executorService = Executors.newCachedThreadPool();    
  13.         List<Future<String>> resultList = new ArrayList<Future<String>>();    
  14.     
  15.         // 创建10个任务并执行    
  16.         for (int i = 0; i < 10; i++) {    
  17.             // 使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中    
  18.             Future<String> future = executorService.submit(new TaskWithResult(i));    
  19.             // 将任务执行结果存储到List中    
  20.             resultList.add(future);    
  21.         }    
  22.         executorService.shutdown();    
  23.     
  24.         // 遍历任务的结果    
  25.         for (Future<String> fs : resultList) {    
  26.             try {    
  27.                 System.out.println(fs.get()); // 打印各个线程(任务)执行的结果    
  28.             } catch (InterruptedException e) {    
  29.                 e.printStackTrace();    
  30.             } catch (ExecutionException e) {    
  31.                 executorService.shutdownNow();    
  32.                 e.printStackTrace();    
  33.                 return;    
  34.             }    
  35.         }    
  36.     }    
  37. }    
  38.     
  39. class TaskWithResult implements Callable<String> {    
  40.     private int id;    
  41.     
  42.     public TaskWithResult(int id) {    
  43.         this.id = id;    
  44.     }    
  45.     
  46.     /**  
  47.      * 任务的具体过程,一旦任务传给ExecutorService的submit方法,则该方法自动在一个线程上执行。  
  48.      *   
  49.      * @return  
  50.      * @throws Exception  
  51.      */    
  52.     public String call() throws Exception {    
  53.         System.out.println("call()方法被自动调用,干活!!!             " + Thread.currentThread().getName());    
  54.         if (new Random().nextBoolean())    
  55.             throw new TaskException("Meet error in task." + Thread.currentThread().getName());    
  56.         // 一个模拟耗时的操作    
  57.         for (int i = 999999999; i > 0; i--)    
  58.             ;    
  59.         return "call()方法被自动调用,任务的结果是:" + id + "    " + Thread.currentThread().getName();    
  60.     }    
  61. }    
  62.     
  63. class TaskException extends Exception {    
  64.     public TaskException(String message) {    
  65.         super(message);    
  66.     }    
  67. }    




执行的结果类似于:


view plain
call()方法被自动调用,干活!!!             pool-1-thread-1  
call()方法被自动调用,干活!!!             pool-1-thread-2  
call()方法被自动调用,干活!!!             pool-1-thread-3  
call()方法被自动调用,干活!!!             pool-1-thread-5  
call()方法被自动调用,干活!!!             pool-1-thread-7  
call()方法被自动调用,干活!!!             pool-1-thread-4  
call()方法被自动调用,干活!!!             pool-1-thread-6  
call()方法被自动调用,干活!!!             pool-1-thread-7  
call()方法被自动调用,干活!!!             pool-1-thread-5  
call()方法被自动调用,干活!!!             pool-1-thread-8  
call()方法被自动调用,任务的结果是:0    pool-1-thread-1  
call()方法被自动调用,任务的结果是:1    pool-1-thread-2  
java.util.concurrent.ExecutionException: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3  
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)  
    at java.util.concurrent.FutureTask.get(FutureTask.java:83)  
    at com.cicc.pts.ExecutorServiceTest.main(ExecutorServiceTest.java:29)  
Caused by: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3  
    at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:57)  
    at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:1)  
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)  
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)  
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)  
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)  
    at java.lang.Thread.run(Thread.java:619)  
可以看见一旦某个task出错,其它的task就停止执行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值