• [温故]图解java多线程设计模式(二)


    一、join & interrupt

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

    1.1:join方法

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

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

    再来简单贴下这几个方法

    
    private String getTop() { // 这里假设getTop需要执行200ms
            try {
                Thread.sleep(200L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "顶部banner位";
        }
    
        private String getLeft() { // 这里假设getLeft需要执行50ms
            try {
                Thread.sleep(50L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "左边栏";
        }
    
        private String getRight() { // 这里假设getRight需要执行80ms
            try {
                Thread.sleep(80L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "右边栏";
        }
    
        private String getUser() { // 这里假设getUser需要执行100ms
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "用户信息";
        }
    

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

    
    // 简单异步获取
        public WebModule getWebModuleMsgSimpleAsync() throws ExecutionException, InterruptedException {
    
            WebModule webModule = new WebModule();
    
            Thread topTask = new Thread(() -> webModule.setTop(this.getTop()));
            Thread leftTask = new Thread(() -> webModule.setLeft(this.getLeft()));
            Thread rightTask = new Thread(() -> webModule.setRight(this.getRight()));
            Thread userTask = new Thread(() -> webModule.setUser(this.getUser()));
    
            //触发各个异步任务
            topTask.start();
            leftTask.start();
            rightTask.start();
            userTask.start();
    
            //等待所有的任务均执行完毕
            topTask.join();
            leftTask.join();
            rightTask.join();
            userTask.join();
    
            return webModule;
        }
    

    测试代码:

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

    测试结果:

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

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

    1.2:interrupt方法

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

    
    public class JoinTest {
    
        private boolean isStop = false;
    
        public static void main(String[] args) throws Exception {
            JoinTest test = new JoinTest();
            Thread loopT = new Thread(test::loopTask);
            loopT.start();
    
            sleep(2000L); //2s后终止线程
            test.setStop(true);
    
            long s = System.currentTimeMillis();
            loopT.join();
            System.out.println("线程终止后,join阻塞时间为:" + (System.currentTimeMillis() - s));
            System.out.println("end~");
        }
    
        public void setStop(boolean stop) {
            isStop = stop;
        }
    
        public void loopTask() {
            while (!isStop) { //若状态为false,则继续执行下面的逻辑,每隔1s打印一次
                sleep(1000L);
                System.out.println("loop trigger ~");
            }
            Thread.currentThread().interrupt(); //在这里终止掉当前线程
            //事实上,在终止掉线程后,还有接下来的逻辑要执行
            long s = System.currentTimeMillis();
            for (int i = 0; i < 1000000; i++) {
                int[] a = new int[100]; //模拟耗时操作,这里不能用sleep了,因为当前线程已经被终止了
            }
            System.out.println("线程终止后,逻辑块运行时间:" + (System.currentTimeMillis() - s));
        }
    
        public static void sleep(long time) {
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    

    执行结果:

    
    loop trigger ~
    loop trigger ~
    线程终止后,逻辑块运行时间:129
    线程终止后,join阻塞时间为:129
    end~
    

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

    
    loop trigger ~
    loop trigger ~
    线程终止后,join阻塞时间为:0
    end~
    

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

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

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

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

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

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

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

    二、线程状态迁移图

    图1

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

     

    图2

     

  • 相关阅读:
    find指令使用手册
    IP封包协议头/TCP协议头/TCP3次握手/TCP4次挥手/UDP协议头/ICMP协议头/HTTP协议(请求报文和响应报文)/IP地址/子网掩码(划分子网)/路由概念/MAC封包格式
    Vmare虚拟机中的3种网络连接方式
    Windows10下Apache2.4配置Django
    网站配色
    js 图片轮播
    Window10下Apache2.4的安装和运行
    sqlite数据库转换为mysql数据库
    windows10 安装 mysql 5.6 教程
    win10 nginx + django +flup 配置
  • 原文地址:https://www.cnblogs.com/hama1993/p/10520901.html
Copyright © 2020-2023  润新知