【Java】多线程系列(三)之阻塞线程的多种方法
来源:互联网 发布:盛和资源股票交易数据 编辑:程序博客网 时间:2024/05/18 10:17
前言:
在某些应用场景下,我们可能需要等待某个线程执行完毕,然后才能进行后续的操作。也就是说,主线程需要等待子线程都执行完毕才能执行后续的任务。例如,当你在计算利用多线程执行几个比较耗时的任务的时候,主线程需要利用这几个线程计算的结果,才能进行后续的操作。那么我们其实就需要等待所有线程执行完毕。
这里,介绍几个常用的方法
线程执行单次的场景下
1,利用Thread类的join()方法
package concurrent;import java.util.ArrayList;import java.util.List;public class JoinTest { public static void main(String[] args) { List<Thread> list=new ArrayList<>(); for(int i=0;i<5;i++){ Thread t=new Thread(){ @Override public void run() { System.out.println(Thread.currentThread().getName()+" is running!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }; list.add(t); t.start(); } for(int i=0;i<5;i++){ try { list.get(i).join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(Thread.currentThread()+" is running!"); }}
程序运行结果如下:
Thread-2 is running!Thread-0 is running!Thread-1 is running!Thread-3 is running!Thread-4 is running!Thread[main,5,main] is running!
注:
对于上面的join()进行线程阻塞的时候,需要注意一下另外一种情况: 上面的5个线程,每次线程start()之后立即调用join()
这样会产生类似于单线程模式,即5个线程序列化形式组织,顺序执行。
2,利用ExecutorService的invokeAll()方法
package concurrent;import java.util.ArrayList;import java.util.List;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ExecutorServiceInvokeAllTest { public static void main(String[] args) { ExecutorService exec=Executors.newFixedThreadPool(5); List<Callable<String>> list=new ArrayList<>(); for(int i=0;i<5;i++){ callableTest cl=new callableTest(); list.add(cl); } try { exec.invokeAll(list); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread()+" is running!"); exec.shutdown(); }}class callableTest implements Callable<String>{ @Override public String call() throws Exception { System.out.println(Thread.currentThread()+" is running!"); Thread.sleep(1000);//模拟线程执行的耗时过程 return null; }}
程序运行结果:
Thread[pool-1-thread-1,5,main] is running!Thread[pool-1-thread-3,5,main] is running!Thread[pool-1-thread-2,5,main] is running!Thread[pool-1-thread-5,5,main] is running!Thread[pool-1-thread-4,5,main] is running!Thread[main,5,main] is running!
invokeAll是一个阻塞方法,会等待任务列表中的所有任务都执行完成。等待所有的任务完成之后,就会返回一个Future的列表,里面记录了每个线程运行之后的结果。一旦ExecutorService.invokeAll()方法产生了异常,线程池中还没有完成的任务会被取消执行。
3,利用Future的get()方法
package concurrent;import java.util.HashSet;import java.util.Set;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class FutureTest { public static void main(String[] args) { ExecutorService exc=Executors.newFixedThreadPool(5); Set<Future<Integer>> set = new HashSet<Future<Integer>>(); for(int i=0;i<5;i++){ callTest cl=new callTest(); Future<Integer> f=exc.submit(cl); set.add(f); } int num=0; for(Future<Integer> f:set){ try { num+=f.get(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(Thread.currentThread()+" is running!"); System.out.println("The value num="+num); exc.shutdown(); }}class callTest implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println(Thread.currentThread()+" is running!"); Thread.sleep(1000); return Integer.valueOf(1); }}
输出结果如下:
Thread[pool-1-thread-1,5,main] is running!Thread[pool-1-thread-3,5,main] is running!Thread[pool-1-thread-2,5,main] is running!Thread[pool-1-thread-4,5,main] is running!Thread[pool-1-thread-5,5,main] is running!Thread[main,5,main] is running!The value num=5
Runnable是执行工作的独立任务,不返回任何结果。如果希望在任务完成之后,能够返回一个值,那么可以实现Callable接口而不是Runnable接口。在Java SE5中引入的Callable是一种具有类型参数的泛型,它的类型参数表示的是从方法call()中返回的值,并且必须使用ExecutorService.submit()方法调用它。
submit()方法会产生Future对象,它用Callable返回的结果的特定类型进行了参数化。你可以使用isDone()方法来查看Future是否已经完成。当任务完成时,就会返回结果,可以调用get()方法来获取返回的结果。当然,也可以不用isDone()方法进行检查就可以直接调用get(),在这种情况下,get()方法就会被阻塞,直至返回结果就绪。
4,利用ExecutorService的awaitTermination()方法
package concurrent;import java.util.Random;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;public class ESawaitTerminationTest { public static void main(String[] args) { ExecutorService exc=Executors.newFixedThreadPool(5); for(int i=0;i<5;i++){ Thread thread=new Thread(){ @Override public void run() { System.out.println(Thread.currentThread()+" is running!"); try { int temp=new Random().nextInt(5000); System.out.println("sleep duration: "+temp+"ms"); sleep(temp); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; exc.execute(thread); } exc.shutdown(); try { while(!exc.awaitTermination(1, TimeUnit.SECONDS)){ System.out.println("Executors is not terminated!"); } System.out.println("Executors is terminated!"); } catch (Exception e) { e.printStackTrace(); } }}
输出结果如下:
Thread[pool-1-thread-2,5,main] is running!Thread[pool-1-thread-1,5,main] is running!Thread[pool-1-thread-3,5,main] is running!sleep duration: 80mssleep duration: 468mssleep duration: 1200msThread[pool-1-thread-5,5,main] is running!sleep duration: 195msThread[pool-1-thread-4,5,main] is running!sleep duration: 2209msExecutors is not terminated!Executors is not terminated!Executors is terminated!
上面的awaitTermination()方法也可以使用isTerminated()方法进行判断,同时在while()循环体内加一个Thread.sleep()方法,即每隔一段时间检查一遍线程池是否终止。这两个方法是同样的效果!
5,利用CountDownLatch
CountDownLatch类可以生成指定数目的锁,每把锁对应一个线程,每个线程执行完毕之后,就可以相应的减少一把锁。当锁的数目减为0之后,相当于所有的锁都已经”归还“,所有线程执行完毕。
package concurrent;import java.util.Random;import java.util.concurrent.CountDownLatch;public class CountDownLatchTest { public static void main(String[] args) { CountDownLatch cl=new CountDownLatch(5); for(int i=0;i<5;i++){ Thread t=new Thread(){ public void run() { System.out.println(Thread.currentThread()+" is running!"); int temp=new Random().nextInt(5000); System.out.println("sleep duration: "+temp+"ms"); try { sleep(temp); cl.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }; t.start(); } try { cl.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread()+" is running!"); }}
输出结果如下:
Thread[Thread-0,5,main] is running!Thread[Thread-3,5,main] is running!Thread[Thread-2,5,main] is running!Thread[Thread-1,5,main] is running!sleep duration: 1105mssleep duration: 1169mssleep duration: 4168mssleep duration: 642msThread[Thread-4,5,main] is running!sleep duration: 1949msThread[main,5,main] is running!
循环业务场景下的使用
很多时候,一些线程为了实现定时更新的功能,就会每隔一段时间,循环执行某个任务。
例如:
web开发中,主线程M,子线程A、B。M需要等待子线程A、B至少各自完成一次任务执行才能进行后面的任务执行。之后子线程A、B就每隔一段时间循环执行任务去更新数据。
那么这种场景下如何实现需求呢?
参见笔者之前的多线程系列中的一篇文章:
【Java】多线程系列(二)之CountDownLatch的使用
- 【Java】多线程系列(三)之阻塞线程的多种方法
- 初见Java多线程(三、线程的阻塞状态)
- java 多线程总结(二) 线程阻塞的方法
- Java多线程开发系列之三:线程这一辈子(线程的生命周期)
- Java基础:多线程(1)--线程的概述、创建线程的方式、线程的多种状态、线程常用的方法
- Java多线程系列(三)-----线程池
- Java多线程之~~~线程安全容器的非阻塞容器
- 多线程之Java线程阻塞与唤醒
- 多线程之Java线程阻塞与唤醒
- Java多线程之线程池(三)
- java多线程(三)之线程池
- java多线程3(线程的阻塞和生命周期)
- 多线程系列三-线程常用方法
- java 多线程学习笔记之 线程实现(线程阻塞)
- 多线程之线程创建的两种方法(Java)
- Java + 线程系列之线程的生命周期(三)
- 多线程学习总结(三)——实现多线程的方法之线程池
- 创建Java多线程的多种方法
- SpringMVC+Mybatis搭建高性能安全站点
- POJ 2526 Center of symmetry 笔记
- 数据结构(Java)--线性表
- Java选择语句中赋值语句
- HUD:平视显示器两文献札记
- 【Java】多线程系列(三)之阻塞线程的多种方法
- string,stringbuffer,stringbuilder
- 小程序rpx
- 【Lua】从入门到不放弃
- PHP中的常用数组函数
- EasyStream(流汇聚,转发工具)
- babelua for vs 2015
- ice-3.6.3源码编译,RedHat6.5
- Dockerfile中CMD的用法