敞开生长之旅!这是我参与「日新方案 12 月更文挑战」的第19天,点击查看活动详情

我是石页兄,本篇不带情感,只聊干货

欢迎重视微信大众号「架构染色」交流和学习


》》》归属专栏:《分布式锁全览》

一、布景

在《分布式锁自动续期的入门级实现-自省 | 精约而不简单》中经过【自省】的办法评论了关于分布式锁自动续期功能的入门级实现办法,其间介绍了如何经过一个额外的线程来完成自动推迟分布式锁的过期时刻,其间提到了几个线程操作的核心技能点,如 interruptsleepvolitale…,有个刚入行的朋友特别细腻,看完之后表明如同懂了又如同差点什么,于是跟笔者又讨论了一些线程的常识,这一篇就整理出来。

二、理还乱?

本篇讨论论题的初始示例如下:

public static void main(String[] args) {
    new Thread(()->{
        try {
            TimeUnit.SECONDS.sleep(10);
            for(int i =0;i<4;i++) {
                System.out.println(LocalDateTime.now() + " myThread exit");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start(); //调用start 来 启动子线程
    //主线程持续一起向下履行
    System.out.printf(LocalDateTime.now() + " main thread exit");
}

打印成果:

2022-12-11T13:17:47.939 main thread exit2022-12-11T13:17:57.877 myThread exit
2022-12-11T13:17:57.878 myThread exit
2022-12-11T13:17:57.878 myThread exit
2022-12-11T13:17:57.878 myThread exit
Process finished with exit code 0
  • 问题:朋友问为啥主线程没有当即退出?

    假如把子线程设置为 deamon Thread,则主线程退出后进程就退出了。

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(10);
                for (int i = 0; i < 4; i++) {
                    System.out.println(LocalDateTime.now() + " myThread exit");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.setDaemon(true);//设置为 deamon
        thread.start(); //调用start 来 启动子线程
        //主线程持续一起向下履行
        System.out.printf(LocalDateTime.now() + " main thread exit");
    }
    

    从成果看主线程是当即完毕,进程快速退出了。

    2022-12-11T13:32:58.154 main thread exit
    Process finished with exit code 0
    
  • 问题:为何如此?

    setDaemon(boolean on)办法的注释可知,所有非 deamon 线程都退出时,进程才退出,也就是说运转中的 deamon 线程不能阻止进程的退出。

    Marks this thread as either a daemon thread or a user thread. The Java Virtual Machine exits when the only threads running are all daemon threads.

  • 问题:线程进入sleep 暂定履行状况,干嘛非要被 interrupt 啊?

    java.lang.Thread.sleep(xxx)办法(注意是类办法),作用是使当时正在履行的线程暂停指定的时刻,上述代码中,当子线程调用TimeUnit.SECONDS.sleep(10)后,子线程会被暂停履行(放弃 CPU 时刻片),等 10 秒后才醒来持续履行,假如期望线程快速醒来并退出,则可将这个子线程interrupt(中止),之后子线程会快速感知到中止状况并抛出 InterruptedException 反常。

    同学,认真思考的样子是这样嘛?

【自省】Thread的理论很简单吧,实战中什么情况要用setDaemon、 interrupt 和 join ?

  • 问题:线程中止是什么机制呢?

    `java.lang.Thread#interrupt()`办法是实例级办法。当线程正在履行`wait()``sleep()``join()`办法时线程是处于【waitting】状况,可理解为内部仍会不断地查看中止状况(interrupt status)的值;`interrupt`办法会改动方针线程的中止状况(interrupt status);所以当被`interrupt()`后,则抛出`InterruptedException`反常。还有一个要点:::反常抛出后,线程内的中止状况会被重置为`false`,所以假如捕获到反常,之后要安排好退出线程的逻辑,否则就可能是bug。有两个办法判断中止状况:
    * Thread实例级办法`java.lang.Thread#isInterrupted()`
    

    :用来查看指定线程的中止状况,true 为中止状况,false 为非中止状况。

    * Thread实例级办法`java.lang.Thread#interrupted()`
    

    :回来线程的中止状况,需特别注意还调用之后还将清除中止状况(置为 false)。

  • 问题:sleep办法会开释 CPU 资源,那会把synchronized锁开释掉嘛?

    例如 synchronized 可使用 对象实例锁 和 类锁这两种,线程中的sleep只是放弃了 CPU 时刻片的使用权,但并不会开释这种同步锁。

    public class SynchronizedDemo {
        /**
         * 实例办法锁 即 this 实例锁
         * @return
         */
        public synchronized String 实例办法锁(){
            return "";
        }
        /**
         * 同 this 实例锁
         * @return
         */
        public String this锁(){
            synchronized (this){
                return "";
            }
        }
        /**
         * 静态办法锁 即类锁
         * @return
         */
        public static synchronized String 静态办法锁(){
            return "";
        }
        /**
         * 类锁,同上
         * @return
         */
        public String 类办法锁(){
            synchronized (SynchronizedDemo.class){
                return  "";
            }
        }
    }
    

三、新的思考

  • 问题:前面除了sleep(),还提到了join(),它是用来干嘛?

    【自省】Thread的理论很简单吧,实战中什么情况要用setDaemon、 interrupt 和 join ?

    java.lang.Thread.join()办法是用于辅佐线程之间协作, 经过join()办法等待方针线程停止。这里有两个关键点:一个是会等待,另一个是方针线程的停止。

  • 问题:为什么要等待方针线程停止呢?

    一般使用子线程有两种状况:

    • 一种状况是把任务交给子线程去做,并不特别关心履行的成果
    • 另外一种则是选用多线程并行的战略来提升核算效率,需求拿到各子线程核算的成果。

    关于第一种状况,只要给了线程停止信号(如设置退出循环的信号、经过interrupt打断sleep,快速完毕线程),当线程内的任务逻辑完毕后,线程后续天然停止了。而第二种状况是子线程核算完毕后线程才处于停止状况,也即当子线程停止之后才可以取成果,而join()办法则是等待置到子线程停止后才回来,而回来之后从子线程中取出的成果才是正确的。

  • 问题:日更写 19 篇了不累嘛?

    累、累、累,我先歇息会儿了,多线程有好多有意思的常识点,我们慢慢聊。

【自省】Thread的理论很简单吧,实战中什么情况要用setDaemon、 interrupt 和 join ?

四、最终说一句

我是石页兄,假如这篇文章对您有帮助,或者有所启示的话,欢迎重视笔者的微信大众号【 架构染色 】进行交流和学习。您的支撑是我坚持写作最大的动力。

欢迎点击链接扫马儿重视、交流。