线程中的常见方法

线程中的常见方法

方法名 static 功能说明 注意
start() 启动一个新的线程,在新的线程运行run方法中的代码 start方法只是让线程进入就绪,里面代码不一定立即运行(CPU的时间片还没分给它)。每个线程对象的start方法只能调用一次,如果调用多次会出现IllegalThreadStateException
run() 新线程启动后会调用的方法 如果在构造Thread对象时传递了Runnable参数,则线程启动后会调用Runnable中的run方法,否则默认不执行任何操作。但可以创建Thread的子类对象,来覆盖默认行为。
join() 等待线程运行结束
join(long n) 等待线程运行结束,最多等待 n 毫秒
getId() 获取线程长整型的id id唯一
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) static 让当前执行的线程休眠n毫秒,休眠时让出CPU的时间片给其他线程
yield() static 提示线程调度器让出当前线程对CPU的使用 主要是为了测试和调试

sleep

  1. 调用 sleep 会让当前线程从 Running (运行状态)进入 TimedWaiting (阻塞状态)

  2. 其他线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出一个 InterruptedException

  3. 睡眠结束后的线程未必会立即得到执行

  4. 建议使用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

例子:

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                log.debug("t1线程进入睡眠...");
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    log.debug("t1线程醒来...");
                    e.printStackTrace();
                }
            }
        };

        t1.start();
        TimeUnit.SECONDS.sleep(1);

        log.debug("1秒后, 主线程调用interrupt()打断t1线程...");
        t1.interrupt();
    }

输出:

21:18:28.772 [t1] DEBUG com.byteframework.learn.test.Test1 - t1线程进入睡眠...
21:18:29.778 [main] DEBUG com.byteframework.learn.test.Test1 - 1秒后, 主线程调用interrupt()打断t1线程...
21:18:29.778 [t1] DEBUG com.byteframework.learn.test.Test1 - t1线程醒来...
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:340)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
	at com.byteframework.learn.test.Test1$1.run(Test1.java:19)

避免 while (true) 时单核CPU占用率100%

    public static void main(String[] args) {
        while (true){
            try {
                TimeUnit.MILLISECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

yield

  1. 调用 yield 方法会让当前线程从 Running(运行状态) 进入Runnable (就绪状态), 然后调度执行其他线程
  2. 具体的实现依赖于操作系统的任务调度器

总结:

相同点:yield 和sleep都会让出CPU的时间片

不同点:yield后线程可能还会立即被任务调度器执行,sleep后线程不会被任务调度器调度。( sleep 后线程从 Running 进入 TimedWaiting , yield 后线程从Running 进入 Runnable)

setPriority

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

测试代码:

    public static void main(String[] args) {
        Runnable task1 = () -> {
            int count = 0;
            for(;;){
                log.debug("task1 ---> {}", count++);
            }
        };

        Runnable task2 = () -> {
            int count = 0;
            for(;;){
                // Thread.yield(); // 让出CPU,结果明显
                log.debug("task2 ---> {}", count++);
            }
        };

        Thread t1 = new Thread(task1, "t1");
        Thread t2 = new Thread(task2, "t2");

        // 设置线程优先级
        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MAX_PRIORITY);

        t1.start();
        t2.start();
    }

总结:

yield 和 setPriority() 都不能真正的控制任务线程的调度, 最终还是由操作系统的任务的调度器来决定哪个线程分到更多的时间片。

join

等待线程运行结束

  • 多线程的典型应用——线程同步,基本用法:
    static int num = 0;
    
    public static void main(String[] args) throws InterruptedException {
        test1();
    }
    
    private static void test1() throws InterruptedException {
        log.debug("开始...");
        Thread t1 = new Thread(() -> {
            log.debug("t1 线程开始...");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("t1 线程休眠结束...");
            num = 10;
        }, "t1");

        t1.start();

        // 主线程同步等待t1线程
        log.debug("主线程同步等待t1线程...");
        t1.join();

        log.debug("num的值: {}", num);
        log.debug("结束");
    }

运行结果:

22:30:28.563 [main] DEBUG com.byteframework.learn.test.Test4 - 开始...
22:30:28.619 [main] DEBUG com.byteframework.learn.test.Test4 - 主线程同步等待t1线程...
22:30:28.619 [t1] DEBUG com.byteframework.learn.test.Test4 - t1 线程开始...
22:30:31.620 [t1] DEBUG com.byteframework.learn.test.Test4 - t1 线程休眠结束...
22:30:31.620 [main] DEBUG com.byteframework.learn.test.Test4 - num的值: 10
22:30:31.625 [main] DEBUG com.byteframework.learn.test.Test4 - 结束
  • 同步等待多个线程的结果,例子:
	static int num1 = 0;
    static int num2 = 0;

    public static void main(String[] args) throws InterruptedException {
        test2();
    }
	
    private static void test2() throws InterruptedException {
        log.debug("开始...");

        Thread t1 = new Thread(() -> {
            log.debug("t1 线程开始...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("t1 线程休眠(1秒)结束...");
            num1 = 10;
        }, "t1");

        Thread t2 = new Thread(() -> {
            log.debug("t2 线程开始...");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("t2 线程休眠(3秒)结束...");
            num2 = 20;
        }, "t2");

        long begin = System.currentTimeMillis();
        t1.start();
        t2.start();

        // 主线程同步等待t1线程
        log.debug("主线程同步等待t1线程...");
        t1.join();

        // 主线程同步等待t1线程
        log.debug("主线程同步等待t2线程...");
        t2.join();

        long end = System.currentTimeMillis();

        log.debug("num1的值: {}, num2的值: {}, 耗时: {} 毫秒", num1, num2, end - begin);
        log.debug("结束");
    }

运行结果:

22:39:25.358 [main] DEBUG com.byteframework.learn.test.Test4 - 开始...
22:39:25.428 [main] DEBUG com.byteframework.learn.test.Test4 - 主线程同步等待t1线程...
22:39:25.429 [t1] DEBUG com.byteframework.learn.test.Test4 - t1 线程开始...
22:39:25.429 [t2] DEBUG com.byteframework.learn.test.Test4 - t2 线程开始...
22:39:26.442 [t1] DEBUG com.byteframework.learn.test.Test4 - t1 线程休眠(1秒)结束...
22:39:26.442 [main] DEBUG com.byteframework.learn.test.Test4 - 主线程同步等待t2线程...
22:39:28.443 [t2] DEBUG com.byteframework.learn.test.Test4 - t2 线程休眠(3秒)结束...
22:39:28.443 [main] DEBUG com.byteframework.learn.test.Test4 - num1的值: 10, num2的值: 20, 耗时: 3015 毫秒
22:39:28.446 [main] DEBUG com.byteframework.learn.test.Test4 - 结束
  • 带时效的等待,例子:
    static int num = 0;

	public static void main(String[] args) throws InterruptedException {
        test3();
    }

	private static void test3() throws InterruptedException {
        log.debug("开始...");
        Thread t1 = new Thread(() -> {
            log.debug("t1 线程开始...");
            try {
                log.debug("t1 进入休眠, 休眠(3秒)结束后将给num赋值为100");
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("t1 线程休眠结束...");
            num = 100;
        }, "t1");

        t1.start();
        long begin = System.currentTimeMillis();

        // 主线程同步等待t1线程
        log.debug("主线程同步等待t1线程, 最多等待1秒...");
        t1.join(1000);

        // log.debug("主线程同步等待t1线程, 最多等待5秒...");
        // t1.join(5000);

        long end = System.currentTimeMillis();
        log.debug("num的值: {}, 耗时: {} 毫秒", num, end - begin);
        log.debug("结束");
    }

等待1秒,运行结果:

22:52:33.375 [main] DEBUG com.byteframework.learn.test.Test4 - 开始...
22:52:33.437 [main] DEBUG com.byteframework.learn.test.Test4 - 主线程同步等待t1线程, 最多等待1秒...
22:52:33.437 [t1] DEBUG com.byteframework.learn.test.Test4 - t1 线程开始...
22:52:33.437 [t1] DEBUG com.byteframework.learn.test.Test4 - t1 进入休眠, 休眠(3秒)结束后将给num赋值为100
22:52:34.451 [main] DEBUG com.byteframework.learn.test.Test4 - num的值: 0, 耗时: 1014 毫秒
22:52:34.453 [main] DEBUG com.byteframework.learn.test.Test4 - 结束
22:52:36.445 [t1] DEBUG com.byteframework.learn.test.Test4 - t1 线程休眠结束...

等待5秒,运行结果:

22:53:03.166 [main] DEBUG com.byteframework.learn.test.Test4 - 开始...
22:53:03.222 [main] DEBUG com.byteframework.learn.test.Test4 - 主线程同步等待t1线程, 最多等待5秒...
22:53:03.223 [t1] DEBUG com.byteframework.learn.test.Test4 - t1 线程开始...
22:53:03.223 [t1] DEBUG com.byteframework.learn.test.Test4 - t1 进入休眠, 休眠(3秒)结束后将给num赋值为100
22:53:06.235 [t1] DEBUG com.byteframework.learn.test.Test4 - t1 线程休眠结束...
22:53:06.236 [main] DEBUG com.byteframework.learn.test.Test4 - num的值: 100, 耗时: 3013 毫秒
22:53:06.240 [main] DEBUG com.byteframework.learn.test.Test4 - 结束

interrupt

  • 打断 sleep、wait、join 阻塞的线程, 示例:
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("t1线程开始休眠, 时长5秒...");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                log.error("t1线程被打断...");
                e.printStackTrace();
            }

        }, "t1");

        t1.start();
        TimeUnit.SECONDS.sleep(2);

        log.debug("主线程1s后调用interrupt方法打断t1线程...");
        t1.interrupt();

        // sleep、wait、join被打断后是以异常的方式表示被打断,打断标记应被置为false
        // 经多此测试发现,打断标记有时为true有时为false, 且为true的几率还很大
        // 增加多个打断标记的输出信息后发现:第一次输出还为true后续输出为false
        // 猜测:调用interrupt()后立即调用isInterrupted()可能打断标记还未来得及清除
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
    }

输出:

20:56:19.981 [t1] DEBUG com.byteframework.learn.test.Test5 - t1线程开始休眠, 时长5秒...
20:56:21.988 [main] DEBUG com.byteframework.learn.test.Test5 - 主线程1s后调用interrupt方法打断t1线程...
20:56:21.988 [t1] ERROR com.byteframework.learn.test.Test5 - t1线程被打断...
20:56:21.988 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:true
20:56:21.990 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
20:56:21.990 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
20:56:21.990 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
20:56:21.990 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:340)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
	at com.byteframework.learn.test.Test5.lambda$main$0(Test5.java:14)
	at java.lang.Thread.run(Thread.java:748)

调整代码,在输出打断标记前,让主线程休眠10毫秒,代码:

        ...
		log.debug("主线程1s后调用interrupt方法打断t1线程...");
        t1.interrupt();

        // 输出打断标记前让主线程休眠10毫秒
        TimeUnit.MILLISECONDS.sleep(10);

        // sleep、wait、join被打断后是以异常的方式表示被打断,打断标记应被置为false
        // 经多此测试发现,打断标记有时为true有时为false, 且为true的几率还很大
        // 增加多个打断标记的输出信息后发现:第一次输出还为true后续输出为false
        // 猜测:调用interrupt()后立即调用isInterrupted()可能打断标记还未来得及清除
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());

发现,打断标记均为 false。输出信息:

21:01:33.464 [t1] DEBUG com.byteframework.learn.test.Test5 - t1线程开始休眠, 时长5秒...
21:01:35.468 [main] DEBUG com.byteframework.learn.test.Test5 - 主线程1s后调用interrupt方法打断t1线程...
21:01:35.468 [t1] ERROR com.byteframework.learn.test.Test5 - t1线程被打断...
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:340)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
	at com.byteframework.learn.test.Test5.lambda$main$0(Test5.java:14)
	at java.lang.Thread.run(Thread.java:748)
21:01:35.484 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
21:01:35.486 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
21:01:35.486 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
21:01:35.486 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
21:01:35.486 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false

猜测:可能是主线程调用 t1.interrupt() 后,t1线程还没有抛出异常,此时t1线程的打断标记应该还是 true,当抛出异常后,打断标记被置为 false。 调整代码, 在catch 中打印打断标记:

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("t1线程开始休眠, 时长5秒...");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                log.error("t1线程被打断...");
                log.debug("catch中, 打断标记:{}", Thread.currentThread().isInterrupted());
                e.printStackTrace();
            }

        }, "t1");

        t1.start();
        TimeUnit.SECONDS.sleep(2);

        log.debug("主线程1s后调用interrupt方法打断t1线程...");
        t1.interrupt();

        // 输出打断标记前让主线程休眠10毫秒
        //  TimeUnit.MILLISECONDS.sleep(10);

        // sleep、wait、join被打断后是以异常的方式表示被打断,打断标记应被置为false
        // 经多此测试发现,打断标记有时为true有时为false, 且为true的几率还很大
        // 增加多个打断标记的输出信息后发现:第一次输出还为true后续输出为false
        // 猜测:调用interrupt()后立即调用isInterrupted()可能打断标记还未来得及清除
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
        log.debug("打断标记:{}", t1.isInterrupted());
    }

和猜测的结果一致,输出:

21:07:02.049 [t1] DEBUG com.byteframework.learn.test.Test5 - t1线程开始休眠, 时长5秒...
21:07:04.059 [main] DEBUG com.byteframework.learn.test.Test5 - 主线程1s后调用interrupt方法打断t1线程...
21:07:04.059 [t1] ERROR com.byteframework.learn.test.Test5 - t1线程被打断...
21:07:04.059 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:true
21:07:04.059 [t1] DEBUG com.byteframework.learn.test.Test5 - catch中, 打断标记:false
21:07:04.061 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
21:07:04.062 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
21:07:04.062 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
21:07:04.062 [main] DEBUG com.byteframework.learn.test.Test5 - 打断标记:false
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:340)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
	at com.byteframework.learn.test.Test5.lambda$main$0(Test5.java:14)
	at java.lang.Thread.run(Thread.java:748)
  • 打断正常的线程,例子:
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                // 这里不进行sleep
                boolean isInterrupted = Thread.currentThread().isInterrupted();
                if (isInterrupted) {
                    log.debug("t1线程被打断了...");
                    log.debug("优雅的退出 while true...");
                    break;
                }
            }
        }, "t1");

        t1.start();

        log.debug("主线程3秒后调用interrupt()打断t1线程");
        TimeUnit.SECONDS.sleep(3);
        t1.interrupt();
    }

输出:

21:31:54.915 [main] DEBUG com.byteframework.learn.test.Test6 - 主线程3秒后调用interrupt()打断t1线程
21:31:57.922 [t1] DEBUG com.byteframework.learn.test.Test6 - t1线程被打断了...
21:31:57.922 [t1] DEBUG com.byteframework.learn.test.Test6 - 优雅的退出 while true...

总结:打断状态 isInterrupted() 可以用来优雅的停止线程,让被打断的线程自己处理后续的操作。

  • 两阶段终止模式,例子:
package com.byteframework.learn.test;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

/**
 * 测试 两阶段终止模式
 */
@Slf4j
public class test7 {

    public static void main(String[] args) throws InterruptedException {

        TowPhaseTermination tpt = new TowPhaseTermination();
        tpt.start();

        TimeUnit.SECONDS.sleep(5);
        tpt.stop();
    }


}


/**
 * 两阶段终止
 */
@Slf4j
class TowPhaseTermination {

    /**
     * 监控线程
     */
    private Thread monitor;

    /**
     * 启动监控线程
     */
    public void start() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                Thread current = Thread.currentThread();

                if (current.isInterrupted()) {
                    log.debug("t1线程被打断了...");
                    log.debug("优雅的退出 while true...");
                    break;
                }

                try {
                    TimeUnit.SECONDS.sleep(1);
                    log.debug("每隔1s执行一次监控...");
                } catch (InterruptedException e) {
                    log.error("t1线程在休眠时被打断, 打断标记:{}", current.isInterrupted());
                    log.error("再次调用 interrupt() 将打断断点标记为: true");
                    // 重置打断标记
                    current.interrupt();

                    e.printStackTrace();
                }
            }
        }, "t1");

        t1.start();

        log.debug("主线程5秒后调用interrupt()打断t1线程");
        TimeUnit.SECONDS.sleep(5);
        t1.interrupt();
    }

    /**
     * 终止监控线程
     */
    public void stop() {
        monitor.interrupt();
    }

}

输出:

22:17:29.456 [main] DEBUG com.byteframework.learn.test.TowPhaseTermination - 主线程5秒后调用interrupt()打断t1线程
22:17:30.454 [t1] DEBUG com.byteframework.learn.test.TowPhaseTermination - 每隔1s执行一次监控...
22:17:31.469 [t1] DEBUG com.byteframework.learn.test.TowPhaseTermination - 每隔1s执行一次监控...
22:17:32.473 [t1] DEBUG com.byteframework.learn.test.TowPhaseTermination - 每隔1s执行一次监控...
22:17:33.477 [t1] DEBUG com.byteframework.learn.test.TowPhaseTermination - 每隔1s执行一次监控...
22:17:34.470 [t1] ERROR com.byteframework.learn.test.TowPhaseTermination - t1线程在休眠时被打断, 打断标记:false
22:17:34.473 [t1] ERROR com.byteframework.learn.test.TowPhaseTermination - 再次调用 interrupt() 将打断断点标记为: true
22:17:34.474 [t1] DEBUG com.byteframework.learn.test.TowPhaseTermination - t1线程被打断了...
22:17:34.474 [t1] DEBUG com.byteframework.learn.test.TowPhaseTermination - 优雅的退出 while true...
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:340)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
	at com.byteframework.learn.test.TowPhaseTermination.lambda$start$0(test7.java:52)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "main" java.lang.NullPointerException
	at com.byteframework.learn.test.TowPhaseTermination.stop(test7.java:76)
	at com.byteframework.learn.test.test7.main(test7.java:19)
  • 打断 park 线程
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("park...");
            LockSupport.park();

            log.debug("unpark...");
            log.debug("打断标记: {}", Thread.currentThread().isInterrupted());
        }, "t1");

        t1.start();

        log.debug("主线程3秒后调用 interrupt() 打断 park线程...");
        TimeUnit.SECONDS.sleep(3);
        t1.interrupt();
    }

输出:

21:00:21.535 [t1] DEBUG com.byteframework.learn.test.test8 - park...
21:00:21.535 [main] DEBUG com.byteframework.learn.test.test8 - 主线程3秒后调用 interrupt() 打断 park线程...
21:00:24.539 [t1] DEBUG com.byteframework.learn.test.test8 - unpark...
21:00:24.539 [t1] DEBUG com.byteframework.learn.test.test8 - 打断标记: true

扩展:park() 被打断后,如果打断标记为 true , 后面再执行 park() 线程也不会停下来, 代码:

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("first park...");
            LockSupport.park();

            log.debug("first unpark...");
            log.debug("打断标记: {}", Thread.currentThread().isInterrupted());

            log.debug("second park...");
            LockSupport.park();
            log.debug("second unpark...");
        }, "t1");

        t1.start();

        log.debug("主线程3秒后调用 interrupt() 打断 park线程...");
        TimeUnit.SECONDS.sleep(3);
        t1.interrupt();
    }

输出:

21:07:05.898 [t1] DEBUG com.byteframework.learn.test.test8 - first park...
21:07:05.898 [main] DEBUG com.byteframework.learn.test.test8 - 主线程3秒后调用 interrupt() 打断 park线程...
21:07:08.913 [t1] DEBUG com.byteframework.learn.test.test8 - first unpark...
21:07:08.913 [t1] DEBUG com.byteframework.learn.test.test8 - 打断标记: true
21:07:08.915 [t1] DEBUG com.byteframework.learn.test.test8 - second park...
21:07:08.915 [t1] DEBUG com.byteframework.learn.test.test8 - second unpark...

从时间上可以看出,地二次的 park() 没有让线程停下来,如果想让线程再次停下来, 需要将打断标记置为 false

线程 Thread 的静态方法 Thread.interrupted() 用于输出当前的打断标记并清除打断标记,代码调整:

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("first park...");
            LockSupport.park();

            log.debug("first unpark...");
            log.debug("打断标记: {}", Thread.interrupted());

            log.debug("second park..., 打断标记: {}", Thread.currentThread().isInterrupted());
            LockSupport.park();
            log.debug("second unpark...");
        }, "t1");

        t1.start();

        log.debug("主线程3秒后调用 interrupt() 打断 park线程...");
        TimeUnit.SECONDS.sleep(3);
        t1.interrupt();
    }

输出:

21:16:50.259 [t1] DEBUG com.byteframework.learn.test.test8 - first park...
21:16:50.259 [main] DEBUG com.byteframework.learn.test.test8 - 主线程3秒后调用 interrupt() 打断 park线程...
21:16:53.274 [t1] DEBUG com.byteframework.learn.test.test8 - first unpark...
21:16:53.274 [t1] DEBUG com.byteframework.learn.test.test8 - 打断标记: true
21:16:53.276 [t1] DEBUG com.byteframework.learn.test.test8 - second park..., 打断标记: false

可以看出第二次的 park() 让线程再次停了下来 ,没有输出:second unpark... 。

setDaemon

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

代码:

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true){
                log.debug("守护线程t1正在执行...");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1");

        // 将线程 t1 设置为守护线程
        t1.setDaemon(true);
        t1.start();

        log.debug("5秒后主线程结束...");
        TimeUnit.SECONDS.sleep(5);
    }

输出:

21:32:01.978 [main] DEBUG com.byteframework.learn.test.Test9 - 5秒后主线程结束...
21:32:01.978 [t1] DEBUG com.byteframework.learn.test.Test9 - 守护线程t1正在执行...
21:32:02.992 [t1] DEBUG com.byteframework.learn.test.Test9 - 守护线程t1正在执行...
21:32:04.007 [t1] DEBUG com.byteframework.learn.test.Test9 - 守护线程t1正在执行...
21:32:05.015 [t1] DEBUG com.byteframework.learn.test.Test9 - 守护线程t1正在执行...
21:32:06.019 [t1] DEBUG com.byteframework.learn.test.Test9 - 守护线程t1正在执行...

Process finished with exit code 0

可以看出 守护线程 t1 并没有一直执行下去,而是在5秒后随主线程一起结束了。

注意:

  1. 垃圾回收器线程就是一种守护线程
  2. Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理玩当前请求。

getState

  • 线程的5种状态(操作系统层面)

    1. 初始状态
      仅在语言层面创建了线程对象,还未与操作系统线程关联
    2. 可运行状态
      有可以称为就绪状态,指线程已经被创建(与操作系统线程关联), 可以由CPU调度执行
    3. 运行状态
      指获取了CPU时间片运行中的状态; 当CPU时间片用完,会从运行状态转换为可运行状态,会导致线程上下文的切换
    4. 阻塞状态
      如果调用了阻塞 API, 如BIO读写文件, 这时该线程不会用到CPU, 会导致线程上下文切换,进入阻塞状态
      等BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至可运行状态
      可运行状态的区别是,对阻塞状态的线程来说只要它们一直不唤醒,调度器就一直不考虑调度它们
    5. 终止状态
      表示线程已经执行完毕,生命周期已经结束,不会再转为其他状态
  • 线程的6种状态(java api层面)
    Thread 类中定义类枚举类 State, 明确定义了线程的6中状态, 源码:

	public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

演示线程的6中状态,代码:

    public static void main(String[] args) throws InterruptedException {
        // 状态:NEW
        // t1 线程创建后没有调用 start() 方法
        Thread t1 = new Thread(() -> {
            log.debug("no start...");
        });

        // 状态:RUNNABLE
        // t2 线程一直在运行
        Thread t2 = new Thread(() ->{
            while (true){

            }
        }, "t2");
        t2.start();

        // 状态:TERMINATED
        // t3 线程打印一句话就结束了
        Thread t3 = new Thread(() -> {
            // log.debug("t3 线程执行结束...");
        }, "t3");
        t3.start();

        // 状态:TIMED_WAITING
        // t4 线程启动后就进入睡眠(足够长时间:999小时), 加入synchronized是为了演示 t6的阻塞状态
        Thread t4 = new Thread(() -> {
            synchronized (TestState.class){
                try {
                    TimeUnit.HOURS.sleep(999);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t4");
        t4.start();

        // 状态:WAITING
        // t5 线程使用 join() 等待t2线程结束
        Thread t5 = new Thread(() -> {
            try {
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t5");
        t5.start();

        // 状态: BLOCKED
        // 对同一个对象上锁 (t4 线程还未释放锁)
        Thread t6 = new Thread(() -> {
            synchronized (TestState.class){
                try {
                    TimeUnit.HOURS.sleep(999);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t6");
        t6.start();

        // 1秒后打印各线程状态
        TimeUnit.SECONDS.sleep(1);

        log.debug("t1线程状态: {}", t1.getState());
        log.debug("t2线程状态: {}", t2.getState());
        log.debug("t3线程状态: {}", t3.getState());
        log.debug("t4线程状态: {}", t4.getState());
        log.debug("t5线程状态: {}", t5.getState());
        log.debug("t6线程状态: {}", t6.getState());
    }

输出:

22:26:34.951 [main] DEBUG com.byteframework.learn.test.TestState - t1线程状态: NEW
22:26:34.957 [main] DEBUG com.byteframework.learn.test.TestState - t2线程状态: RUNNABLE
22:26:34.957 [main] DEBUG com.byteframework.learn.test.TestState - t3线程状态: TERMINATED
22:26:34.957 [main] DEBUG com.byteframework.learn.test.TestState - t4线程状态: TIMED_WAITING
22:26:34.957 [main] DEBUG com.byteframework.learn.test.TestState - t5线程状态: WAITING
22:26:34.957 [main] DEBUG com.byteframework.learn.test.TestState - t6线程状态: BLOCKED
posted on 2024-12-23 15:07  屋蓝  阅读(6)  评论(0)    收藏  举报