一、join & interrupt

这俩方法属于线程对象里的方法,属于线程本身的操作。

1.1:join方法

用于等待一个线程的终止,等待期间将会阻塞,直到被等待的线程终止结束。

所以join可以用来做多任务异步处理,比如还是拿利用CompletableFuture优化程序的执行效率这篇里的第一个例子做优化,这篇文章里使用线程池的future模式进行多任务异步处理,现在使用join改写下:

再来简单贴下这几个方法


  1. private String getTop() { // 这里假设getTop需要执行200ms
  2. try {
  3. Thread.sleep(200L);
  4. } catch (InterruptedException e) {
  5. e.printStackTrace();
  6. }
  7. return "顶部banner位";
  8. }
  9. private String getLeft() { // 这里假设getLeft需要执行50ms
  10. try {
  11. Thread.sleep(50L);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. return "左边栏";
  16. }
  17. private String getRight() { // 这里假设getRight需要执行80ms
  18. try {
  19. Thread.sleep(80L);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. return "右边栏";
  24. }
  25. private String getUser() { // 这里假设getUser需要执行100ms
  26. try {
  27. Thread.sleep(100L);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. return "用户信息";
  32. }

然后现在使用简单的线程做异步处理:


  1. // 简单异步获取
  2. public WebModule getWebModuleMsgSimpleAsync() throws ExecutionException, InterruptedException {
  3. WebModule webModule = new WebModule();
  4. Thread topTask = new Thread(() -> webModule.setTop(this.getTop()));
  5. Thread leftTask = new Thread(() -> webModule.setLeft(this.getLeft()));
  6. Thread rightTask = new Thread(() -> webModule.setRight(this.getRight()));
  7. Thread userTask = new Thread(() -> webModule.setUser(this.getUser()));
  8. //触发各个异步任务
  9. topTask.start();
  10. leftTask.start();
  11. rightTask.start();
  12. userTask.start();
  13. //等待所有的任务均执行完毕
  14. topTask.join();
  15. leftTask.join();
  16. rightTask.join();
  17. userTask.join();
  18. return webModule;
  19. }

测试代码:


  1. @Test
  2. public void testSimpleASync() throws Exception {
  3. // 同步方法测试,预估耗时200ms
  4. long start = System.currentTimeMillis();
  5. WebModule module = webHome.getWebModuleMsgSimpleAsync();
  6. System.out.println("通过异步方法获取首页全部信息消耗时间:" + (System.currentTimeMillis() - start) + "ms");
  7. System.out.println("结果为:" + module.toString());
  8. }

测试结果:


  1. 通过异步方法获取首页全部信息消耗时间:272ms
  2. 结果为:top: 顶部banner位; left: 左边栏; right: 右边栏; user: 用户信息

比预估的要多72ms,经过后来的测试,发现这72ms耗时发生在线程创建的时候,以及后续线程状态转换带来的消耗,下面等待异步结束的时间约等于200ms,符合预期。

1.2:interrupt方法

用于主动终止一个线程,线程本身调用该方法后,视为已终止状态,join解除阻塞,下面来用interrupt和join来做个实验:


  1. public class JoinTest {
  2. private boolean isStop = false;
  3. public static void main(String[] args) throws Exception {
  4. JoinTest test = new JoinTest();
  5. Thread loopT = new Thread(test::loopTask);
  6. loopT.start();
  7. sleep(2000L); //2s后终止线程
  8. test.setStop(true);
  9. long s = System.currentTimeMillis();
  10. loopT.join();
  11. System.out.println("线程终止后,join阻塞时间为:" + (System.currentTimeMillis() - s));
  12. System.out.println("end~");
  13. }
  14. public void setStop(boolean stop) {
  15. isStop = stop;
  16. }
  17. public void loopTask() {
  18. while (!isStop) { //若状态为false,则继续执行下面的逻辑,每隔1s打印一次
  19. sleep(1000L);
  20. System.out.println("loop trigger ~");
  21. }
  22. Thread.currentThread().interrupt(); //在这里终止掉当前线程
  23. //事实上,在终止掉线程后,还有接下来的逻辑要执行
  24. long s = System.currentTimeMillis();
  25. for (int i = 0; i < 1000000; i++) {
  26. int[] a = new int[100]; //模拟耗时操作,这里不能用sleep了,因为当前线程已经被终止了
  27. }
  28. System.out.println("线程终止后,逻辑块运行时间:" + (System.currentTimeMillis() - s));
  29. }
  30. public static void sleep(long time) {
  31. try {
  32. Thread.sleep(time);
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }

执行结果:


  1. loop trigger ~
  2. loop trigger ~
  3. 线程终止后,逻辑块运行时间:129
  4. 线程终止后,join阻塞时间为:129
  5. end~

即便线程被终止了,后面的逻辑也会触发,join依旧会选择阻塞,直到后续逻辑执行完毕,事实上,大部分任务都可以及时的终止,比如第一个例子,异步出去的任务,最终都会执行完成,线程变为终止状态,join都可以顺利结束,但是反观上例,如果没人及时的设置isStop的值,程序会一直执行下去,没有终止态,join会无止境的终止下去,这里提一下stop,线程的stop方法已被官方标记为“不建议使用”的方法,如果把上例的interrupt的调用换成stop,来看看其运行结果:


  1. loop trigger ~
  2. loop trigger ~
  3. 线程终止后,join阻塞时间为:0
  4. end~

可以看到,线程终止后的后续逻辑均没有触发,等于说stop是一种很粗暴的终止线程的方式,一旦被stop,那么里面的业务逻辑将直接断掉,因此官方并不推荐使用该方法来终止线程。

而interrupt,仅仅是对目标线程发送了了一个中断信号(改变了线程的中断状态而已),当目标线程再次通过obj.wait、thread.sleep、thread.join方法进入阻塞状态时,接收到该信号,就会抛出InterruptedException异常,这时候需要业务方自行处理或者直接抛出,以结束线程阻塞状态(这里需要注意的是被obj.wait方法阻塞时,抛出该异常需要目标线程再次获得实例对象obj的锁才行)。

上述三个“需要花费时间”的方法均抛出了InterruptedException异常,针对这些特性,想要完成以下操作就非常方便了:

①取消wait方法等待notify/notifyAll的处理

②取消在sleep方法指定时间内停止的处理

③取消join方法等待其他线程终止的处理

取消之后所做的处理,取决于需求,可能会终止线程,或者通知用户已取消,或者终止当前处理进入下一个处理阶段。

二、线程状态迁移图

图1

上面的图太多太杂,我们通过对一些可以影响线程状态的操作的分类,来简化一下上面的图:

图2

[温故]图解java多线程设计模式(二)的更多相关文章

  1. [温故]图解java多线程设计模式(一)

    去年看完的<图解java多线程设计模式>,可惜当时没做笔记,导致后来忘了许多东西,打算再温习下这本书,顺便在这里记录一下~  1.顺序执行.并行.并发 顺序执行:多个操作按照顺序依次执行. ...

  2. 《图解Java多线程设计模式》读书笔记

    略读中...后面详读的时候,补充经典图片和文字说明

  3. 图解java多线程设计模式之一一synchronized实例方法体

    synchronized实例方法体和synchronized代码块 synchronied void method(){ ....... } 这个等同于下面将方法体用synchronized(this ...

  4. java多线程设计模式

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt220 java多线程设计模式 java语言已经内置了多线程支持,所有实现Ru ...

  5. Java多线程(二)关于多线程的CPU密集型和IO密集型这件事

    点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...

  6. 简述Java多线程(二)

    Java多线程(二) 线程优先级 Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行. 优先级高的不一定先执行,大多数情况是这样的. 优 ...

  7. Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)

    一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...

  8. java多线程系列(二)

    对象变量的并发访问 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我 ...

  9. java多线程系列(二)---对象变量并发访问

    对象变量的并发访问 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我 ...

随机推荐

  1. [mysql]设置Ubuntu上的MySQL可以远程访问

    今天在win10上用django连接安装在Ubuntu上的MySQL上,始终提示错误(can not connect mysql),但是在Ubuntu上访问是没有问题的.于是开始查找原因: 1. 33 ...

  2. UI / UX设计师如何玩转用户心理学原理?

    以下内容由Mockplus团队翻译整理,仅供学习交流,Mockplus是更快更简单的原型设计工具. 众所周知,心理学在APP的用户体验设计中起着非常重要的作用.通过了解我们的设计是如何被感知的,我们可 ...

  3. Mybatis之Configuration初始化(配置文件.xml的解析)

    源码解读第一步我觉着应该从Mybatis如何解析配置文件开始. 1.先不看跟Spring集成如何解析,先看从SqlSessionFactoryBuilder如果解析的. String resouce ...

  4. Executing a Finite-Length Task in the Background

    [Executing a Finite-Length Task in the Background] Apps that are transitioning to the background can ...

  5. SpringBoot配置Druid

    Druid是Java语言中最好的数据库连接池.Druid能够提供强大的监控和扩展功能.关于详细介绍可查看http://www.iteye.com/magazines/90 SpringBoot中集成D ...

  6. context:propertyPlaceholder

    Activates replacement of ${...} placeholders by registering a PropertySourcesPlaceholderConfigurer w ...

  7. Exception in thread "main" java.lang.Error: Unresolved compilation problem

    初学java,使用eclipse编译时,可能会遇到如下图所示的编译错误(Exception in thread "main" java.lang.Error: Unresolved ...

  8. [Selenium With C#基础教程] Lesson-03 超级链接

    作者:Surpassme 来源:http://www.jianshu.com/p/83809943e751 声明:本文为原创文章,如需转载请在文章页面明显位置给出原文链接,谢谢. 超级链接或链接是We ...

  9. java和json互转

    在网页中想后台传递多个数据时,有时数据还是多个动态列表,数据很复杂时,JavaScript程序员喜欢把他们作为json串进行处理,后台收到后需要对json字符串进行解析,幸好有JSON-lib,这个J ...

  10. 在SharePoint列表中使用动态筛选条件[今日][Today]

    如果在SharePoint使用了日历控件或者其他列表中有时间字段,用户经常希望能够动态使用条件字段进行筛选,例如希望筛选出开始日期是今天的事件.未来三日的事件. SharePoint的列表筛选条件支持 ...