多线程并发库高级应用 之 java5中的线程并发库--线程池、Callable&Future
笔记摘要:
这里首先介绍了java5中的并发的小工具包:automatic,然后介绍了线程池的概念,对使用java5的方式创建不同形式的线程进行了演示,
之后介绍了两个 对象:Callable和Future,用于获取线程执行后的结果,对于线程锁技术则在另外一篇文章中介绍。
Java5中的线程并发库都在java.util.concurrent包及子包中
一、java.util.concurrent.atomic包:
类的小工具包,支持在单个变量上解除锁的线程安全编程
1、如果同一个变量要被多个线程访问,则可以使用该包中的类:
AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference
2、AtomicIntegerArray:操作数组里面的某个整数
3、同样该包中提供了可以用于反射操作的类:
AtomicReferenceFieldUpdater、
AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater
它们可以提供对关联字段类型的访问。
二、线程池:
在线程池的编程模式下,任务是提交给整个线程池,而不是直接交给某个线程,线程池在拿到任务后,它就在内部找有无空闲的线程,
再把任务交给内部某个空闲的线程,这就是封装。
记住:任务是提交给整个线程池,一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。
示例:
创建固定大小的线程池、创建缓存线程池 、 用线程池创建定时器
创建单一线程池(始终保证线程池中会有一个线程在。当某线程死去,会找继任者)
注意:
定时器中总是相对时间,我们要想指定具体时间的方法:比如明天早上10点钟执行,则可以使用明天早上10点的时间减去
当前的时间,得到时间间隔
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class ThreadPoolTest { public static void main(String[] args){ //创建固定大小的线程池,这里只能完成3个任务 //ExecutorService threadPool = Executors.newFixedThreadPool(3); //创建缓存线程池,根据任务来自动创建线程的数量,可以完成创建的所有任务 //ExecutorService threadPool = Executors.newCachedThreadPool(); //创建单一线程池(始终保持线程池中有一个线程存活。当唯一线程死去,会创建新的继任者、 ExecutorService threadPool = Executors.newSingleThreadExecutor(); for(int i=1;i<=10;i++){ //内部类不能访问外部类的局部变量,所以i要定义为final,又由于i++. //所以在循环内部定义一个变量接收i final int task = i; threadPool.execute(new Runnable() { @Override public void run() { for(int j=1;j<=10;j++){ System.out.println(Thread.currentThread().getName() +" is looping of "+ j+" for task of " +task); } } }); } //验证10个任务都提交给了线程池 System.out.println("all of 10 tasks have committed! "); //threadPool.shutdown(); //等任务完成后,杀死线程、 //threadPool.shutdownNow(); //立即停止线程 //用线程池启动定时器 Executors.newScheduledThreadPool(3).schedule( new Runnable() { //任务 @Override public void run() { System.out.println("bombing!"); } }, 5, //5秒以后执行 TimeUnit.SECONDS); //单位 //在某个时间执行一次后,再指定后续的执行间隔时间 Executors.newScheduledThreadPool(3).scheduleAtFixedRate(new Runnable(){ @Override public void run() { System.out.println("bombing!"); } }, 10, //第一次在10秒时爆炸 3, //以后每隔3秒爆炸一次。 TimeUnit.SECONDS); } }
三、Callable & Future:用于获取线程执行完的结果
1、Callable 接口类似于Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,
并且无法抛出经过检查的异常,而Callable返回结果并且可能抛出异常的任务。
2、Future 接口表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能
使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。
3、Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
Callable要采用ExecutorService的submit方法提交,返回的future对象可以取消任务、
4、CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
示例:
这里数据的获取好比同时种了号几块地的麦子,然后等待收割,秋收时,哪块先熟,先收割哪块。
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class CallableAndFuture { public static void main(String[] args){ //创建一个单独的线程 ExecutorService threadPool = Executors.newSingleThreadExecutor(); //future泛型与Callable的类型一致 Future<String> future = threadPool.submit(new Callable<String>(){ @Override public String call() throws Exception { Thread.sleep(3000); return "hello"; } }); System.out.println("等待結果……"); //在指定时timeout内等待,未等到抛出TimeoutException //System.out.println("拿到结果:" + future.get(long timeout,TimeUnit unit)); try { System.out.println("拿到结果:" + future.get()); //获取线程执行后的结果 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } //CompletionService用于提交一组Callable任务, //其take方法返回已完成的一个Callable任务对应的Future对象。 ExecutorService threadPool2 = Executors.newFixedThreadPool(10); CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPool2); //创建10任务 for(int i=1;i<=10;i++){ final int seq = i; //将任务提交给线程池 completionService.submit(new Callable<Integer>(){ @Override public Integer call() throws Exception { Thread.sleep(new Random().nextInt(5000)); return seq; } }); } //获取结果,哪个结果先返回就先获得 for(int i=0;i<10;i++){ try { System.out.println(completionService.take().get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } }