java并发编程JUC:二、线程API精讲(start和run、sleep和yield)+两阶段终止模式(interrupted、isInterrupted、interrupt、park)

start和run

java并发编程JUC:二、线程API精讲(start和run、sleep和yield)+两阶段终止模式(interrupted、isInterrupted、interrupt、park)_第1张图片

当我们只调用run时

public static void main(String[] args) {
	 Thread t1 = new Thread("t1") {
	 @Override
	 public void run() {
		 log.debug(Thread.currentThread().getName());
		 FileReader.read(Constants.MP4_FULL_PATH);
		 }
	 };
	 t1.run();
	 log.debug("do other things ...");
}

输出:

19:39:14 [main] c.TestStart - main
19:39:14 [main] c.FileReader - read [1.mp4] start ...
19:39:18 [main] c.FileReader - read [1.mp4] end ... cost: 4227 ms
19:39:18 [main] c.TestStart - do other things ...

可以看到程序仍在 main 线程运行, FileReader.read() 方法调用还是同步的

当我们改为调用start时

19:41:30 [main] c.TestStart - do other things ...
19:41:30 [t1] c.TestStart - t1
19:41:30 [t1] c.FileReader - read [1.mp4] start ...
19:41:35 [t1] c.FileReader - read [1.mp4] end ... cost: 4542 ms

可以看到程序在 t1 线程运行, FileReader.read() 方法调用是异步的

对比总结

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

sleep和yield

java并发编程JUC:二、线程API精讲(start和run、sleep和yield)+两阶段终止模式(interrupted、isInterrupted、interrupt、park)_第2张图片
sleep

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

yield

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

interrupted、isInterrupted、interrupt

java并发编程JUC:二、线程API精讲(start和run、sleep和yield)+两阶段终止模式(interrupted、isInterrupted、interrupt、park)_第3张图片

其中的清除打断标记指的是无论打断标记是true还是false,interrupted都将它变为false。

两阶段终止模式

在一个线程 T1 中如何“优雅”终止线程 T2?这里的【优雅】指的是给 T2 一个料理后事的机会。

错误思路

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

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

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

代码1

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test1")
public class test1 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();
        Thread.sleep(3500);
        tpt.stop();
    }
}

@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination{
    private Thread monitorThread;

    public void start(){
        monitorThread = new Thread(()->{
            while(true){
                Thread current = Thread.currentThread();
                if(current.isInterrupted()){
                    log.debug("料理后事");
                    break;
                }
                try{
                    Thread.sleep(1000);
                    log.debug("执行监控记录");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    current.interrupt();
                }
            }
        },"monitor");
        monitorThread.start();
    }

    public void stop(){
        monitorThread.interrupt();
    }
}

运行结果为:
java并发编程JUC:二、线程API精讲(start和run、sleep和yield)+两阶段终止模式(interrupted、isInterrupted、interrupt、park)_第4张图片

代码2

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test1")
public class test1 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();
        Thread.sleep(3500);
        tpt.stop();
    }
}

@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination{
    private Thread monitorThread;

    public void start(){
        monitorThread = new Thread(()->{
            while(true){
                Thread current = Thread.currentThread();
                if(current.interrupted()){
                    log.debug("料理后事");
                    break;
                }
                try{
                    Thread.sleep(1000);
                    log.debug("执行监控记录");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    current.interrupt();
                }
            }
        },"monitor");
        monitorThread.start();
    }

    public void stop(){
        monitorThread.interrupt();
    }
}

java并发编程JUC:二、线程API精讲(start和run、sleep和yield)+两阶段终止模式(interrupted、isInterrupted、interrupt、park)_第5张图片

代码3

如果代码改为

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test1")
public class test1 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();
        Thread.sleep(3500);
        tpt.stop();
    }
}

@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination{
    private Thread monitorThread;

    public void start(){
        monitorThread = new Thread(()->{
            while(true){
                Thread current = Thread.currentThread();
                if(current.interrupted()){
                    log.debug("料理后事");
                    break;
                }
                try{
                    Thread.sleep(1000);
                    log.debug("执行监控记录");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"monitor");
        monitorThread.start();
    }

    public void stop(){
        monitorThread.interrupt();
    }
}

运行结果为
java并发编程JUC:二、线程API精讲(start和run、sleep和yield)+两阶段终止模式(interrupted、isInterrupted、interrupt、park)_第6张图片

代码4

如果将代码改为:

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test1")
public class test1 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();
        Thread.sleep(3500);
        tpt.stop();
    }
}

@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination{
    private Thread monitorThread;

    public void start(){
        monitorThread = new Thread(()->{
            while(true){
                Thread current = Thread.currentThread();
                if(current.isInterrupted()){
                    log.debug("料理后事");
                    break;
                }
                try{
                    Thread.sleep(1000);
                    log.debug("执行监控记录");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"monitor");
        monitorThread.start();
    }

    public void stop(){
        monitorThread.interrupt();
    }
}

则运行结果为:
java并发编程JUC:二、线程API精讲(start和run、sleep和yield)+两阶段终止模式(interrupted、isInterrupted、interrupt、park)_第7张图片

对比总结

1、isInterrupted + catch里面 interrupt ,正常退出
2、interrupted + catch里面 interrupt, 正常退出
3、isInterrupted,不能正常退出
4、interrupted, 不能正常退出

第一和第二种情况是因为interrupt会重置打断标记。
第三和第四种情况是因为interrupt打断正在睡眠的线程,会清除打断标记。所以无论这个判断方法是否清除打断标记,标记都是false。

打断park线程

打断 park 线程, 不会清空打断状态

private static void test3() throws InterruptedException {
	 Thread t1 = new Thread(() -> {
		 log.debug("park...");
		 LockSupport.park();
		 log.debug("unpark...");
		 log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
	 }, "t1");
	 t1.start();
	 sleep(0.5);
	 t1.interrupt();
}

输出

21:11:52.795 [t1] c.TestInterrupt - park... 
21:11:53.295 [t1] c.TestInterrupt - unpark... 
21:11:53.295 [t1] c.TestInterrupt - 打断状态:true

如果打断标记已经是 true, 则 park 会失效

private static void test4() {
	 Thread t1 = new Thread(() -> {
	 for (int i = 0; i < 5; i++) {
		 log.debug("park...");
		 LockSupport.park();
		 log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
		 }
	 });
	 t1.start();
	 sleep(1);
	 t1.interrupt();
}

输出:

21:13:48.783 [Thread-0] c.TestInterrupt - park... 
21:13:49.809 [Thread-0] c.TestInterrupt - 打断状态:true 
21:13:49.812 [Thread-0] c.TestInterrupt - park... 
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true 
21:13:49.813 [Thread-0] c.TestInterrupt - park... 
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true 
21:13:49.813 [Thread-0] c.TestInterrupt - park... 
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true 
……

你可能感兴趣的:(java并发编程,java,开发语言,jvm,后端)