• 线程常用方法与守护线程


    常用方法

    方法名static功能说明注意
    start()启动一个新线程,在新的线程运行 run 方法中的代码start 方法只是让线程进入就绪,里面代码不一定立刻运行(CPU 的时间片还没分给它)。每个线程对象的start方法只能调用一次,如果调用了多次会出现IllegalThreadStateException
    run()新线程启动后会调用的方法如果在构造 Thread 对象时传递了 Runnable 参数,则线程启动后会调用 Runnable 中的 run 方法,否则默认不执行任何操作。但可以创建 Thread 的子类对象,来覆盖默认行为
    join()等待线程运行结束
    join(long n)等待线程运行结束,最多等待 n 毫秒
    getId()获取线程长整型的 idid 唯一
    getName()获取线程名
    setName(String)修改线程名
    getPriority()获取线程优先级
    setPriority(int)修改线程优先级java中规定线程优先级是1~10 的整数,较大的优先级能提高该线程被 CPU 调度的机率
    getState()获取线程状态Java 中线程状态是用 6 个 enum 表示,分别为:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
    isInterrupted()判断是否被打断不会清除 打断标记
    isAlive()线程是否存活(还没有运行完毕)
    interrupt()打断线程如果被打断线程正在 sleep,wait,join 会导致被打断的线程抛InterruptedException,并清除打断标记;如果打断的正在运行的线程,则会设置 打断标记;park 的线程被打断,也会设置打断标记
    interrupted()static判断当前线程是否被打断会清除 打断标记
    currentThread()static获取当前正在执行的线程
    sleep(long n)staticstatic让当前执行的线程休眠n毫秒,休眠时让出 cpu的时间片给其它线程
    yield()static提示线程调度器让出当前线程对CPU的使用主要是为了测试和调试

    start 与 run

    start 方法只是让线程进入就绪,里面代码不一定立刻运行(CPU 的时间片还没分给它)。每个线程对象的start方法只能调用一次,如果调用了多次会出现 IllegalThreadStateException

    测试代码 :

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.io.FileReader;
    
    /**
     * @author 王天赐
     * @title: TestStartAndRun
     * @projectName hm-juc-codes
     * @description: 测试start与run方法
     * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
     * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
     * @create: 2022-06-21 15:13
     */
    @Slf4j(topic = "c.TestStartAndRun")
    @SuppressWarnings("all")
    public class TestStartAndRun {
    
        /**
         * 模拟读取
         */
        public static void reader() {
            long start = System.currentTimeMillis();
            int sum = 0;
            for (int i = 0; i < 10000_000_00; i++) {
                sum += sum;
            }
            long end = System.currentTimeMillis();
            log.debug("reader cost {} ms ... ", (end - start));
        }
    
        public static void main(String[] args) {
    
            Thread t1 = new Thread("t1") {
                @Override
                public void run() {
                    // 获取当前线程的名字
                    log.debug("{} running ... ", Thread.currentThread().getName());
                    reader();
                }
            };
    
            t1.start();
            log.debug("do other thing ... ");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    运行结果

    C:\dev\Java\jdk1.8.0_152\bin\java.exe -javaagent:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\lib\idea_rt.jar=6195:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\bin -Dfile.encoding=UTF-8 -classpath C:\dev\Java\jdk1.8.0_152\jre\lib\charsets.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\deploy.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\javaws.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jce.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfr.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jsse.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\management-agent.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\plugin.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\resources.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\rt.jar;F:\JavaCode\hm-juc-codes\juc-case-01\target\classes;F:\MavenRepo\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;F:\MavenRepo\ch\qos\logback\logback-classic\1.2.10\logback-classic-1.2.10.jar;F:\MavenRepo\ch\qos\logback\logback-core\1.2.10\logback-core-1.2.10.jar;F:\MavenRepo\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;F:\MavenRepo\cn\hutool\hutool-all\5.7.20\hutool-all-5.7.20.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core-benchmarks\1.35\jmh-core-benchmarks-1.35.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core\1.35\jmh-core-1.35.jar;F:\MavenRepo\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;F:\MavenRepo\org\apache\commons\commons-math3\3.2\commons-math3-3.2.jar;F:\MavenRepo\org\openjdk\jmh\jmh-java-benchmark-archetype\1.35\jmh-java-benchmark-archetype-1.35.jar cn.knightzz.example.e2.method.TestStartAndRun
    16:07:46.045 [main] DEBUG c.TestStartAndRun - do other thing ... 
    16:07:46.045 [t1] DEBUG c.TestStartAndRun - t1 running ... 
    16:07:46.301 [t1] DEBUG c.TestStartAndRun - reader cost 254 ms ... 
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如上面的运行结果所示 :

    • t1 线程开启后并没有立马执行 , 而是执行了主线程的方法, 后再执行的 t1 线程的方法
    • 注意 running 是 t1线程执行的 !
    • t1 线程和主线程是并行执行的 !

    而使用 run 执行时, running 是 在 主线程执行的, 并没有新开一个线程!

    img

    代码实例 :

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j(topic = "c.TestStartStatus")
    @SuppressWarnings("all")
    public class TestStartStatus {
    
        public static void main(String[] args) {
            Thread t1 = new Thread("t1") {
                @Override
                public void run() {
                    // 获取当前线程的名字
                    log.debug("{} running ... ", Thread.currentThread().getName());
                }
            };
    
            log.debug("t1 : {} " , t1.getState());
            t1.start();
            log.debug("t1 : {} " , t1.getState());
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    执行结果 :

    C:\dev\Java\jdk1.8.0_152\bin\java.exe -javaagent:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\lib\idea_rt.jar=14539:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\bin -Dfile.encoding=UTF-8 -classpath C:\dev\Java\jdk1.8.0_152\jre\lib\charsets.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\deploy.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\javaws.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jce.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfr.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jsse.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\management-agent.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\plugin.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\resources.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\rt.jar;F:\JavaCode\hm-juc-codes\juc-case-01\target\classes;F:\MavenRepo\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;F:\MavenRepo\ch\qos\logback\logback-classic\1.2.10\logback-classic-1.2.10.jar;F:\MavenRepo\ch\qos\logback\logback-core\1.2.10\logback-core-1.2.10.jar;F:\MavenRepo\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;F:\MavenRepo\cn\hutool\hutool-all\5.7.20\hutool-all-5.7.20.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core-benchmarks\1.35\jmh-core-benchmarks-1.35.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core\1.35\jmh-core-1.35.jar;F:\MavenRepo\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;F:\MavenRepo\org\apache\commons\commons-math3\3.2\commons-math3-3.2.jar;F:\MavenRepo\org\openjdk\jmh\jmh-java-benchmark-archetype\1.35\jmh-java-benchmark-archetype-1.35.jar cn.knightzz.example.e2.method.TestStartStatus
    17:28:18.600 [main] DEBUG c.TestStartStatus - t1 : NEW 
    17:28:18.604 [main] DEBUG c.TestStartStatus - t1 : RUNNABLE 
    17:28:18.604 [t1] DEBUG c.TestStartStatus - t1 running ... 
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以看到上面的执行结果以及代码 , 当执行 start()方法以后, 线程的状态从 NEW 变成了 RUNNABLE

    总结 :

    • 直接调用 run 是在主线程中执行了 run,没有启动新的线程
    • 使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码

    sleep 与 yield

    sleep

    • 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞
    • 当前线程可以使用 interrupt方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
    • 睡眠结束后的线程未必会立刻得到执行
    • 建议用 TimeUnit sleep 代替 Thread sleep 来获得更好的可读性

    sleep 状态

    代码示例 :

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j(topic = "c.TestSleep")
    @SuppressWarnings("all")
    public class TestSleep {
    
        public static void main(String[] args){
    
    
            Thread t1 = new Thread("t1") {
                @Override
                public void run() {
                    log.debug("t1 running ... ");
                    log.debug("into sleep ... ");
                    // 睡眠的是当前线程
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        log.debug("t1 wake up ... ");
                        throw new RuntimeException(e);
                    }
                }
            };
    
            log.debug("t1 status is {} ... " , t1.getState());
            t1.start();
            log.debug("t1 status is {} ... " , t1.getState());
            // 打断其他正在睡眠的线程
            log.debug("do other things ... ");
        }
    }
    C:\dev\Java\jdk1.8.0_152\bin\java.exe -javaagent:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\lib\idea_rt.jar=6691:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\bin -Dfile.encoding=UTF-8 -classpath C:\dev\Java\jdk1.8.0_152\jre\lib\charsets.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\deploy.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\javaws.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jce.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfr.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jsse.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\management-agent.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\plugin.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\resources.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\rt.jar;F:\JavaCode\hm-juc-codes\juc-case-01\target\classes;F:\MavenRepo\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;F:\MavenRepo\ch\qos\logback\logback-classic\1.2.10\logback-classic-1.2.10.jar;F:\MavenRepo\ch\qos\logback\logback-core\1.2.10\logback-core-1.2.10.jar;F:\MavenRepo\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;F:\MavenRepo\cn\hutool\hutool-all\5.7.20\hutool-all-5.7.20.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core-benchmarks\1.35\jmh-core-benchmarks-1.35.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core\1.35\jmh-core-1.35.jar;F:\MavenRepo\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;F:\MavenRepo\org\apache\commons\commons-math3\3.2\commons-math3-3.2.jar;F:\MavenRepo\org\openjdk\jmh\jmh-java-benchmark-archetype\1.35\jmh-java-benchmark-archetype-1.35.jar cn.knightzz.example.e2.method.TestSleep
    20:01:43.076 [main] DEBUG c.TestSleep - t1 status is NEW ... 
    20:01:43.079 [main] DEBUG c.TestSleep - t1 status is RUNNABLE ... 
    20:01:43.079 [main] DEBUG c.TestSleep - do other things ... 
    20:01:43.079 [t1] DEBUG c.TestSleep - t1 running ... 
    20:01:43.079 [t1] DEBUG c.TestSleep - into sleep ... 
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    示例图

    img

    让主线程睡眠的原因是因为 防止 t1线程还没有睡眠, t1线程的状态信息已经被打印出来, 此时t1线程的状态依然是 RUNNABLE

    sleep 打断

    代码示例 :

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @author 王天赐
     * @title: TestInterceptor
     * @projectName hm-juc-codes
     * @description: 测试sleep打断
     * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
     * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
     * @create: 2022-06-21 20:39
     */
    @Slf4j(topic = "c.TestSleep")
    @SuppressWarnings("all")
    public class TestInterceptor {
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread t1 = new Thread("t1") {
                @Override
                public void run() {
                    log.debug("t1 running ... ");
                    // 睡眠的是当前线程
                    try {
                        log.debug("start sleep ... ");
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        log.debug("t1 wake up ... ");
                        throw new RuntimeException(e);
                    }
                }
            };
    
            t1.start();
            // 主线程睡眠是要保证 t1 线程在执行 interrupt 方法时已经在睡眠
            Thread.sleep(1000);
            t1.interrupt();
            log.debug("do other things ... ");
    
        }
    }
    C:\dev\Java\jdk1.8.0_152\bin\java.exe -javaagent:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\lib\idea_rt.jar=6365:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\bin -Dfile.encoding=UTF-8 -classpath C:\dev\Java\jdk1.8.0_152\jre\lib\charsets.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\deploy.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\javaws.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jce.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfr.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jsse.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\management-agent.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\plugin.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\resources.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\rt.jar;F:\JavaCode\hm-juc-codes\juc-case-01\target\classes;F:\MavenRepo\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;F:\MavenRepo\ch\qos\logback\logback-classic\1.2.10\logback-classic-1.2.10.jar;F:\MavenRepo\ch\qos\logback\logback-core\1.2.10\logback-core-1.2.10.jar;F:\MavenRepo\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;F:\MavenRepo\cn\hutool\hutool-all\5.7.20\hutool-all-5.7.20.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core-benchmarks\1.35\jmh-core-benchmarks-1.35.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core\1.35\jmh-core-1.35.jar;F:\MavenRepo\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;F:\MavenRepo\org\apache\commons\commons-math3\3.2\commons-math3-3.2.jar;F:\MavenRepo\org\openjdk\jmh\jmh-java-benchmark-archetype\1.35\jmh-java-benchmark-archetype-1.35.jar cn.knightzz.example.e2.method.TestInterceptor
    20:51:30.022 [t1] DEBUG c.TestSleep - t1 running ... 
    20:51:30.024 [t1] DEBUG c.TestSleep - start sleep ... 
    20:51:31.023 [t1] DEBUG c.TestSleep - t1 wake up ... 
    20:51:31.023 [main] DEBUG c.TestSleep - do other things ... 
    Exception in thread "t1" java.lang.RuntimeException: java.lang.InterruptedException: sleep interrupted
    	at cn.knightzz.example.e2.method.TestInterceptor$1.run(TestInterceptor.java:30)
    Caused by: java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at cn.knightzz.example.e2.method.TestInterceptor$1.run(TestInterceptor.java:27)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    运行结果如上, 可以看到, t1 线程在休眠1s以后, 就被打断了

    TimeUnit Sleep

    增加可读性

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    import javax.swing.*;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author 王天赐
     * @title: TestUnit
     * @projectName hm-juc-codes
     * @description: 测试TimeUnit
     * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
     * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
     * @create: 2022-06-21 20:59
     */
    @Slf4j(topic = "c.TestTimeUnit")
    @SuppressWarnings("all")
    public class TestTimeUnit {
    
        public static void main(String[] args) throws InterruptedException {
    
            log.debug("start ...");
            TimeUnit.SECONDS.sleep(10);
            // Thread.sleep()
            log.debug("end ...");
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    yield

    • 调用 yield 会让当前线程从 Running进入 Runnable就绪状态,然后调度执行其它线程
    • 具体的实现依赖于操作系统的任务调度器
    • 目的是把当前线程的CPU执行权让出去, 但是CPU下一次可能还会把时间片分给

    img

    线程优先级

    • 线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
    • 如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用

    CPU闲的情况

    代码示例 :

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @author 王天赐
     * @title: TestPriority
     * @projectName hm-juc-codes
     * @description: 测试进程优先级
     * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
     * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
     * @create: 2022-06-22 20:33
     */
    @SuppressWarnings("all")
    @Slf4j(topic = "c.TestPriority")
    public class TestPriority {
    
        public static void main(String[] args) {
    
            Runnable task1 = new Runnable() {
                @Override
                public void run() {
                    log.debug("task1 running ... ");
                }
            };
    
            Runnable task2 = new Runnable() {
                @Override
                public void run() {
                    log.debug("task2 running ... ");
                }
            };
    
            Thread t1 = new Thread(task1, "t1");
            Thread t2 = new Thread(task2, "t1");
    
            // 设置优先级
            t1.setPriority(Thread.MAX_PRIORITY);
    
            t1.start();
            t2.start();
    
            log.debug("main ended ... ");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    C:\dev\Java\jdk1.8.0_152\bin\java.exe -javaagent:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\lib\idea_rt.jar=8641:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\bin -Dfile.encoding=UTF-8 -classpath C:\dev\Java\jdk1.8.0_152\jre\lib\charsets.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\deploy.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\javaws.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jce.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfr.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jsse.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\management-agent.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\plugin.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\resources.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\rt.jar;F:\JavaCode\hm-juc-codes\juc-case-01\target\classes;F:\MavenRepo\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;F:\MavenRepo\ch\qos\logback\logback-classic\1.2.10\logback-classic-1.2.10.jar;F:\MavenRepo\ch\qos\logback\logback-core\1.2.10\logback-core-1.2.10.jar;F:\MavenRepo\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;F:\MavenRepo\cn\hutool\hutool-all\5.7.20\hutool-all-5.7.20.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core-benchmarks\1.35\jmh-core-benchmarks-1.35.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core\1.35\jmh-core-1.35.jar;F:\MavenRepo\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;F:\MavenRepo\org\apache\commons\commons-math3\3.2\commons-math3-3.2.jar;F:\MavenRepo\org\openjdk\jmh\jmh-java-benchmark-archetype\1.35\jmh-java-benchmark-archetype-1.35.jar cn.knightzz.example.e2.method.TestPriority
    20:38:32.626 [main] DEBUG c.TestPriority - main ended ... 
    20:38:32.626 [t1] DEBUG c.TestPriority - task2 running ... 
    20:38:32.626 [t1] DEBUG c.TestPriority - task1 running ... 
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以看到 , 上面的运行结果 : 尽管 t1线程设置了最大的优先级, 但是仍然会出现t2线程先运行的情况

    CPU繁忙的情况

    但是 如果 线程繁忙的时候 :

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @author 王天赐
     * @title: TestPriority
     * @projectName hm-juc-codes
     * @description: 测试进程优先级
     * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
     * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
     * @create: 2022-06-22 20:33
     */
    @SuppressWarnings("all")
    @Slf4j(topic = "c.TestPriority")
    public class TestPriority {
    
        public static void main(String[] args) {
    
            Runnable task1 = new Runnable() {
                @Override
                public void run() {
                    // log.debug("task1 running ... ");
                    int count = 0;
                    while (true) {
                        Thread.yield();
                        log.debug("task1 ==================> {}", count);
                        count++;
                    }
                }
            };
    
            Runnable task2 = new Runnable() {
                @Override
                public void run() {
                    int count = 0;
                    while (true) {
                        Thread.yield();
                        log.debug("task2 ==================> {}", count);
                        count++;
                    }
                }
            };
    
            Thread t1 = new Thread(task1, "t1");
            Thread t2 = new Thread(task2, "t1");
    
            // 设置优先级
            t1.setPriority(Thread.MAX_PRIORITY);
    
            t1.start();
            t2.start();
    
            log.debug("main ended ... ");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    img

    如上图可以看到 , task1 的数字要比 task2 的数字大, 说明task1获取CPU时间片的次数比较多

    join 方法详解

    join 方法的作用的等待对应线程结束, 比如 t1.join() 作用是等待t1线程结束

    案例 1 一般情况

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.TimeUnit;
    
    @SuppressWarnings("all")
    @Slf4j(topic = "c.TestJoin")
    public class TestJoin {
    
        private static int value = 0;
    
        public static void main(String[] args) throws InterruptedException {
    
            method1();
    
        }
    
        private static void method1() throws InterruptedException {
            log.debug("method1 start ... ");
            Thread t1 = new Thread(() -> {
                try {
                    log.debug("sleep start ... ");
                    TimeUnit.SECONDS.sleep(1);
                    value = 10;
                    log.debug("sleep end ... ");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
    
            t1.start();
            log.debug("value 结果 {} ", value);
            log.debug("main end ... ");
        }
    }
    C:\dev\Java\jdk1.8.0_152\bin\java.exe -javaagent:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\lib\idea_rt.jar=5463:D:\DevApp\JetBrain\apps\IDEA-U\ch-0\221.5787.30\bin -Dfile.encoding=UTF-8 -classpath C:\dev\Java\jdk1.8.0_152\jre\lib\charsets.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\deploy.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\javaws.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jce.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfr.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\jsse.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\management-agent.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\plugin.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\resources.jar;C:\dev\Java\jdk1.8.0_152\jre\lib\rt.jar;F:\JavaCode\hm-juc-codes\juc-case-01\target\classes;F:\MavenRepo\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;F:\MavenRepo\ch\qos\logback\logback-classic\1.2.10\logback-classic-1.2.10.jar;F:\MavenRepo\ch\qos\logback\logback-core\1.2.10\logback-core-1.2.10.jar;F:\MavenRepo\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;F:\MavenRepo\cn\hutool\hutool-all\5.7.20\hutool-all-5.7.20.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core-benchmarks\1.35\jmh-core-benchmarks-1.35.jar;F:\MavenRepo\org\openjdk\jmh\jmh-core\1.35\jmh-core-1.35.jar;F:\MavenRepo\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;F:\MavenRepo\org\apache\commons\commons-math3\3.2\commons-math3-3.2.jar;F:\MavenRepo\org\openjdk\jmh\jmh-java-benchmark-archetype\1.35\jmh-java-benchmark-archetype-1.35.jar cn.knightzz.example.e2.method.TestJoin
    21:56:45.562 [main] DEBUG c.TestJoin - method1 start ... 
    21:56:45.594 [Thread-0] DEBUG c.TestJoin - sleep start ... 
    21:56:45.594 [main] DEBUG c.TestJoin - value 结果 0 
    21:56:45.595 [main] DEBUG c.TestJoin - main end ... 
    21:56:46.606 [Thread-0] DEBUG c.TestJoin - sleep end ... 
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    可以看到上面的结果, 输出的value值是 0, 这是因为主线程和t1线程是并行执行的, 不存在谁等谁的情况

    img

    案例 2 join

    我们可以使用 join 来等待线程结束

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.TimeUnit;
    
    @SuppressWarnings("all")
    @Slf4j(topic = "c.TestJoin")
    public class TestJoin {
    
        private static int value = 0;
    
        public static void main(String[] args) throws InterruptedException {
    
            method1();
    
        }
    
        private static void method1() throws InterruptedException {
            log.debug("method1 start ... ");
            Thread t1 = new Thread(() -> {
                try {
                    log.debug("sleep start ... ");
                    TimeUnit.SECONDS.sleep(1);
                    value = 10;
                    log.debug("sleep end ... ");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
    
            t1.start();
            t1.join();
            log.debug("value 结果 {} ", value);
            log.debug("main end ... ");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    img

    从调用方的角度 :

    • 需要等待结果返回, 才能继续执行的是 同步
    • 不需要等待结果返回就可以继续执行的是 异步

    案例3 join方法

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.TimeUnit;
    
    @SuppressWarnings("all")
    @Slf4j(topic = "c.TestJoin2")
    public class TestJoin2 {
    
        private static int value = 0;
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread t1 = new Thread(){
                @Override
                public void run() {
    
                    try {
                        // 睡眠 1s
                        TimeUnit.SECONDS.sleep(1);
                        // 修改 value 的值
                        value = 10;
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
    
            Thread t2 = new Thread(){
                @Override
                public void run() {
    
                    try {
                        // 睡眠 1s
                        TimeUnit.SECONDS.sleep(2);
                        // 修改 value 的值
                        value = 20;
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
    
            t1.start();
            t2.start();
            t1.join();
            t2.join();
    
            log.debug("value : {} " , value);
            log.debug("do other thing ... ");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    问 : 上面的代码执行时间是 3s 还是 2s ?

    img

    案例4 有时效的join

    我们可以通过指定传入参数指定等待的时间 : t1.join(3000);

    package cn.knightzz.example.e2.method;
    
    import cn.knightzz.example.e1.test.Test2;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.TimeUnit;
    
    @SuppressWarnings("all")
    @Slf4j(topic = "c.TestJoin2")
    public class TestJoin3 {
    
        static int value = 0;
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread t1 = new Thread(){
                @Override
                public void run() {
    
                    // 睡眠 5s
                    try {
                        TimeUnit.SECONDS.sleep(5);
                        value = 10;
                        log.debug("sleep end ... ");
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
    
            t1.start();
            log.debug("thread start ... ");
            t1.join(3000);
            log.debug("value : {}", value);
            log.debug("join end ... ");
    
        }
    }
    C:\Dev\jdk8\bin\java.exe -javaagent:D:\DevApp\JetBrains\ToolBox\apps\IDEA-U\ch-0\221.5787.30\lib\idea_rt.jar=4754:D:\DevApp\JetBrains\ToolBox\apps\IDEA-U\ch-0\221.5787.30\bin -Dfile.encoding=UTF-8 -classpath C:\Dev\jdk8\jre\lib\charsets.jar;C:\Dev\jdk8\jre\lib\ext\access-bridge-64.jar;C:\Dev\jdk8\jre\lib\ext\cldrdata.jar;C:\Dev\jdk8\jre\lib\ext\dnsns.jar;C:\Dev\jdk8\jre\lib\ext\dtfj.jar;C:\Dev\jdk8\jre\lib\ext\dtfjview.jar;C:\Dev\jdk8\jre\lib\ext\jaccess.jar;C:\Dev\jdk8\jre\lib\ext\localedata.jar;C:\Dev\jdk8\jre\lib\ext\nashorn.jar;C:\Dev\jdk8\jre\lib\ext\sunec.jar;C:\Dev\jdk8\jre\lib\ext\sunjce_provider.jar;C:\Dev\jdk8\jre\lib\ext\sunmscapi.jar;C:\Dev\jdk8\jre\lib\ext\sunpkcs11.jar;C:\Dev\jdk8\jre\lib\ext\traceformat.jar;C:\Dev\jdk8\jre\lib\ext\zipfs.jar;C:\Dev\jdk8\jre\lib\jce.jar;C:\Dev\jdk8\jre\lib\jsse.jar;C:\Dev\jdk8\jre\lib\management-agent.jar;C:\Dev\jdk8\jre\lib\resources.jar;C:\Dev\jdk8\jre\lib\rt.jar;C:\Dev\jdk8\bin\Dll02.dll;K:\CodeSpace\HMCodeSpace\hm-juc-codes\juc-case-01\target\classes;D:\Repos\mavenRepos\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;D:\Repos\mavenRepos\ch\qos\logback\logback-classic\1.2.10\logback-classic-1.2.10.jar;D:\Repos\mavenRepos\ch\qos\logback\logback-core\1.2.10\logback-core-1.2.10.jar;D:\Repos\mavenRepos\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\Repos\mavenRepos\cn\hutool\hutool-all\5.7.20\hutool-all-5.7.20.jar;D:\Repos\mavenRepos\org\openjdk\jmh\jmh-core-benchmarks\1.35\jmh-core-benchmarks-1.35.jar;D:\Repos\mavenRepos\org\openjdk\jmh\jmh-core\1.35\jmh-core-1.35.jar;D:\Repos\mavenRepos\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;D:\Repos\mavenRepos\org\apache\commons\commons-math3\3.2\commons-math3-3.2.jar cn.knightzz.example.e2.method.TestJoin3
    21:16:55.011 [main] DEBUG c.TestJoin2 - thread start ... 
    21:16:58.017 [main] DEBUG c.TestJoin2 - value : 0
    21:16:58.019 [main] DEBUG c.TestJoin2 - join end ... 
    21:17:00.015 [Thread-3] DEBUG c.TestJoin2 - sleep end ... 
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    如上面可以看到

    interrupt 方法详解

    打断sleep, wait, join 的线程

    sleep, wait, join这几个方法都会让线程进入阻塞状态 , 打断 sleep 的线程, 会清空打断状态(打断状态会重置为 false ),以 sleep 为例

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author 王天赐
     * @title: TestInterrupt
     * @projectName hm-juc-codes
     * @description: 打断sleep线程
     * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
     * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
     * @create: 2022-06-27 21:37
     */
    @SuppressWarnings("all")
    @Slf4j(topic = "c.TestInterrupt")
    public class TestInterrupt {
    
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread t1 = new Thread(() -> {
    
                try {
                    TimeUnit.SECONDS.sleep(5); // sleep, wait, join 被打断后, 打断标记会被清空
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }, "t1");
    
            t1.start();
    
            // 主线程睡眠1s , 保证打断时, t1线程已经sleep
            TimeUnit.SECONDS.sleep(1);
    
            // 打断
            t1.interrupt();
            // 查看打断状态
            log.debug("打断标记 : {}" , t1.isInterrupted());
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    img

    如上图可以看到 , t1 线程在睡眠中被打断, 并且打断标记被重新设置为 false

    t1.isInterrupted() 如果线程已经被打断返回 true, 否则返回 false , 睡眠中被打断的线程会抛出异常的

    打断正常的线程

    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author 王天赐
     * @title: TestInterrupt2
     * @projectName hm-juc-codes
     * @description: 测试打断正常线程
     * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
     * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
     * @create: 2022-06-28 14:45
     */
    @SuppressWarnings("all")
    @Slf4j(topic = "c.TestInterrupt2")
    public class TestInterrupt2 {
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread t1 = new Thread(() -> {
                while(true) {
                    Thread thread = Thread.currentThread();
                    boolean interrupted = thread.isInterrupted();
                    if (interrupted) {
                        log.debug("打断状态 : {} " , interrupted);
                        break;
                    }
                }
            }, "t1");
    
            t1.start();
            // 休眠一秒, 保证t1线程处于运行状态
            TimeUnit.SECONDS.sleep(1);
            t1.interrupt();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    img

    如上图可以看到 : 正常线程运行时被打断, 此时打断状态是 true

    模式之两阶段终止

    问题

    • 在线程t1里面正确的打断线程t2 , 并且让t2线程有一个可以处理被打断后的方法

    错误思路

    • 使用线程对象的 stop() 方法停止线程

      • stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁, 其它线程将永远无法获取锁
    • 使用 System.exit(int) 方法停止线程

      • 目的仅是停止一个线程,但这种做法会让整个程序都停止

    两阶段终止模式

    https://blog.csdn.net/thetimelyrain/article/details/114587111

    • LockSupport.park() 可以让当前线程阻塞
    package cn.knightzz.example.e2.method;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.LockSupport;
    
    @SuppressWarnings("all")
    @Slf4j(topic = "c.TestInterrupt2")
    public class TestPark {
    
        public static void main(String[] args) throws InterruptedException {
            method1();
        }
    
        private static void method1() throws InterruptedException {
    
            Thread t1 = new Thread(() -> {
    
                log.debug("thread park ... ");
                LockSupport.park(); // 暂停当前线程
                log.debug("thread unpark ... ");
                log.debug("打断状态 : {} " , Thread.currentThread().isInterrupted());
            }, "t1");
    
            t1.start();
            // 睡眠1s
            TimeUnit.SECONDS.sleep(1);
            // 打断 t1 线程的park
            t1.interrupt();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    img

    可以看到上面的运行结果 , 主线程休眠 1s 以后, 开始打断

    LockSupport.park(); 方法细节 :

    1. 第一次执行park被打断以后, isInterrupted 会变为 true
    2. 此时, 如果再次调用 park() 方法就无法阻塞线程了.
     private static void method1() throws InterruptedException {
    
            Thread t1 = new Thread(() -> {
    
                log.debug("thread park ... ");
                LockSupport.park(); // 暂停当前线程
                log.debug("thread unpark ... ");
                log.debug("打断状态 : {} " , Thread.currentThread().isInterrupted());
    
                // park() 方法细节 :
                // 1. 第一次执行park被打断以后, isInterrupted 会变为 true
                // 2. 此时, 如果再次调用 park() 方法就无法阻塞线程了.
    
                LockSupport.park();
                log.debug("thread unpark ... ");
    
            }, "t1");
    
            t1.start();
            // 睡眠1s
            TimeUnit.SECONDS.sleep(1);
            // 打断 t1 线程的park
            t1.interrupt();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    img

    如上图可以看到, 当打断线程后, 再次调用 park 方法无法阻塞线程

    • 可以使用 Thread.interrupted() 清除打断状态
     private static void method1() throws InterruptedException {
    
            Thread t1 = new Thread(() -> {
    
                log.debug("thread park ... ");
                LockSupport.park(); // 暂停当前线程
                log.debug("thread unpark ... ");
                log.debug("打断状态 : {} " , Thread.currentThread().isInterrupted());
    
                // park() 方法细节 :
                // 1. 第一次执行park被打断以后, isInterrupted 会变为 true
                // 2. 此时, 如果再次调用 park() 方法就无法阻塞线程了.
    
                LockSupport.park();
                log.debug("thread unpark ... ");
    
                // 清除打断状态
                log.debug("clear interrupt status ... ");
                Thread.currentThread().interrupted();
                log.debug("thread park ... ");
                LockSupport.park();
                log.debug("thread unpark ... ");
    
            }, "t1");
    
            t1.start();
            // 睡眠1s
            TimeUnit.SECONDS.sleep(1);
            // 打断 t1 线程的park
            t1.interrupt();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    img

    如上图可以看到 , 在清除打断状态以后, 重新调用 park() 方法, 此时 线程被阻塞

    LockSupport.park 总结

    1. LockSupport.park() 用来阻塞当前线程, 并且可以被 interrupt() 方法打断
    2. 第一次执行park被打断以后, isInterrupted 会变为 true
    3. 此时, 如果再次调用 park() 方法就无法阻塞线程了.
    4. 但是我们可以调用 Thread.currentThread().interrupted(); 清除线程打断状态

    不推荐使用方法

    不推荐使用的方法,这些方法已过时,容易破坏同步代码块,造成线程死锁 :

    • stop() 停止线程运行
    • suspend() 挂起(暂停)线程运行
    • resume() 恢复线程运行

    主线程与守护线程

    默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。

    package cn.knightzz.example.e2.thread;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.TimeUnit;
    
    @SuppressWarnings("all")
    @Slf4j(topic = "c.TestDaemon")
    public class TestDaemon {
    
        public static void main(String[] args) throws InterruptedException {
    
            method1();
    
            TimeUnit.SECONDS.sleep(1);
            log.debug("main end ... ");
        }
    
        private static void method1() {
    
            Thread t1 = new Thread(() -> {
    
                log.debug("thread start ... ");
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.debug("thread end ... ");
            }, "t1");
    
            // 设置当前线程为守护线程
            // 做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束
            t1.setDaemon(true);
            t1.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    img

    如上图, 根据上面的代码 当t1线程启动后 休眠3s 才会执行结束, 但是由于 t1 线程被设置为守护线程, 所以

    当main方法结束后, 即使 t1线程并未执行结束, 但是也被强制关闭

  • 相关阅读:
    牛客算法题——密码验证合格程序
    (二)docker:建立oracle数据库mount startup
    R语言用logistic逻辑回归和AFRIMA、ARIMA时间序列模型预测世界人口
    架构设计流程
    DRF的认证组件(源码分析)
    大数据学习之Spark性能优化
    Rust教程6:并发编程和线程通信
    Vuex的核心组成、版本问题及store.js的使用、 Vuex中存值、取值以及获取变量值、异步同步操作和Vuex后台交互
    记一次Max模型导入到GIS平台歪了,尺寸不对过程分析
    RabbitMQ集群配置以及负载均衡配置
  • 原文地址:https://blog.csdn.net/weixin_40040107/article/details/125509787