并发编程 | 线程中断与阻塞(内含demo)

关于 线程中断与阻塞 的其他疑问:

  1. 抛出InterruptedException后,interrupt标志位会复位?所以在catch InterruptedException 异常时要注意恢复现场(就是重新把这个被阻塞的线程在interrupt一遍)?对!
  2. 线程被interrupt之后还能恢复就绪状态吗?还没搞明白
  3. wait,sleep,join时,调interrupt会抛异常?对!

1. 证明Thread.interrupt并不能真正使线程阻塞

package com.example.juc.interrupt;

/**
 * 练习线程中断与阻塞
 * Thread类自带interrupt方法
 */
public class ThreadInterrupt {

    // 1.测试给某个线程设置中断后,是否仍然能执行任务
    // 2.练习使用Thread的sleep,join,wait方法
    // 3.查看某线程设置了interrupt之后,再执行sleep,join,wait方法效果会如何


    private static void task() {

        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }

        Thread.currentThread().interrupt();

        for (int i = 11; i < 20; i++) {
            System.out.println(i);
        }

    }

    public static void main(String[] args) {
        new Thread(() -> {
            task();
        }).start();
    }
}

2. interrupt方法不能单独使用

interrupt真正用法,让被中断线程搭配Thread.currentThread().isInterrupted()使用

package com.example.juc.interrupt;

/**
 * 练习线程中断与阻塞
 * Thread类自带interrupt方法
 */
public class ThreadInterrupt {

    // interrupt真正用法,让被中断线程搭配Thread.currentThread().isInterrupted()使用

    private static void task(String taskName) {

        while(!Thread.currentThread().isInterrupted()) {
            System.out.println(taskName + "  :  " + 11);
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            task("a");
        });

        t1.start();

        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
            }
            t1.interrupt(); // 模拟中断:这里t1的打印会有两种结果:t1完全打印不出来或者打印一段就停了
        }).start();
    }
}

在这里插入图片描述

3. 设置被中断线程的执行优先级,方便观察中断(其实是变种的demo2)

package com.example.juc.interrupt;

/**
 * 练习线程中断与阻塞
 * Thread类自带interrupt方法
 */
public class ThreadInterrupt {

    // interrupt真正用法,让被中断线程搭配Thread.currentThread().isInterrupted()使用

    private static void task(String taskName) {
        while(!Thread.currentThread().isInterrupted()) {
            System.out.println(taskName + "  :  " + 11);
        }

        System.out.println("中断了。。。。。。");
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            task("a");
        });
        t1.setPriority(10); // 调高了t1的优先级,让t1先跑,方便观察t2通知t1中断的处理效果
        t1.start();

        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
            }
            t1.interrupt(); // 中断t1

            // 新问题:线程处理完中断是否会复位。。
        }).start();

    }
}

4. 线程中断的其他实现方式

4.1 用volitle实现线程中断
package com.example.juc.interrupt;

/**
 * 通过volitle关键字实现中断
 */
public class InterruptByVolitle {

    private static volatile boolean isStop = false;

    // 模拟一个线程通知另一个线程中断

    public static void main(String[] args) {
        
        new Thread(() -> {
            while(true) {
                if(isStop) {
                    System.out.println(Thread.currentThread().getId() + "线程中断了" + isStop);
                    break;
                } else {
                    System.out.println(Thread.currentThread().getId() + "----");
                }
            }

        }).start();


        // 对主线程休眠的新理解:休眠会让出CPU的执行权,这样做是为了让其他线程有更大的概率获取到时间片
        // 从而倾向呈现预测的效果
        try {
            System.out.println(Thread.currentThread().getId() + "线程准备休眠"); 
            Thread.currentThread().sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        
        new Thread(() -> {
            isStop = true;
        }).start();
    }


}

4.2 用AtomicBoolean实现线程中断
package com.example.juc.interrupt;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 通过AtomicBoolean关键字实现中断
 */
public class InterruptByVolitle {

//    private static volatile boolean isStop = false;
    private static AtomicBoolean isStop = new AtomicBoolean(false);

    // 模拟一个线程通知另一个线程中断

    public static void main(String[] args) {

        new Thread(() -> {
            while(true) {
                if(isStop.get()) {
                    System.out.println(Thread.currentThread().getId() + "线程中断了" + isStop.get());
                    break;
                } else {
                    System.out.println(Thread.currentThread().getId() + "----");
                }
            }

        }).start();


        // 对主线程休眠的新理解:休眠会让出CPU的执行权,这样做是为了让其他线程有更大的概率获取到时间片
        // 从而倾向呈现预测的效果
        try {
            System.out.println(Thread.currentThread().getId() + "线程准备休眠");
            Thread.currentThread().sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        new Thread(() -> {
            isStop.set(true);
        }).start();
    }


}

5. 高频面试题:使用interrupt的注意事项

在这里插入图片描述

5.1 问题demo

先演示一个有问题的写法:通知一个已经处于sleep/wait/join状态的线程中断
有问题的点在于:catch 了 InterruptedException时没有恢复中断状态,这会导致程序一直死循环

package com.example.juc.interrupt;
/**
 * Thread#interrupt的使用注意事项
 */
public class Interrupt2 {

    private static synchronized void task() {
        while(true) {
            if(Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getId() + "线程中断了");
                break;
            }

            // 线程在没被中断的情况下,每间隔4s打印一次
            try {
                Thread.currentThread().sleep(4000);
            } catch (InterruptedException e) {
                System.out.println("异常!"); // 此处异常处理要小心!!!
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getId() + "----");
        }
    }

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            task();
        });
        t1.start();

        try {
            System.out.println(Thread.currentThread().getId() + "线程准备休眠");
            Thread.currentThread().sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            t1.interrupt(); //通知t1中断
        }).start();
    }
}

5.2 问题demo的执行结果

执行结果:
在这里插入图片描述

5.3 解决方案

异常处理时的正确做法:中断恢复
在这里插入图片描述

5.4 Thread#interrupt文档详解

查看Thread#interrupt的方法描述:
如果线程被中断时已经调用了wait,sleep,join方法,那么会在接收到InterruptedException后擦除中断标志位
在这里插入图片描述

6. 演示线程wait/notify时使用interrupt报错

package com.example.juc.interrupt;
/**
 * Thread#interrupt的使用注意事项
 */
public class Interrupt2 {

    private synchronized void task(Interrupt2 obj) {
        while(true) {
            if(Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getId() + "线程中断了");
                break;
            }

            // 线程在没被中断的情况下,每间隔4s打印一次
            try {
                obj.wait(4000);  // wait可行,拿到了对象锁,不然会报IllegalMonitorStateException异常
                //this.wait(4000);  // wait可行,拿到的是对象锁
                //Thread.currentThread().wait(4000);   // wait用错了
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();  // 正确的做法:在catch InterruptedException 后进行中断恢复
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getId() + "----");
        }
    }

    public static void main(String[] args) {

        Interrupt2 obj = new Interrupt2();

        Thread t1 = new Thread(() -> {
            obj.task(obj);
        });
        t1.start();

        try {
            System.out.println(Thread.currentThread().getId() + "线程准备休眠");
            Thread.currentThread().sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            t1.interrupt(); //通知t1中断
        }).start();
    }
}

在这里插入图片描述

6.1 总结:使用wait/notify 必须与 synchronized 一起使用

源码如下
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值