void | interrupt() | 中断此线程 |
---|---|---|
static boolean | interrupted() | 测试当前线程是否已被中断 |
boolean | isInterrupted() | 测试此线程是否已经被中断 |
首先
其次
**定义:**中断一个正在运行的线程,(run方法还没有执行结束),普通线程会在run方法执行结束后自动停止,我们的中断其实就是更改线程的状态,想让线程终止,只有run方法执行完毕,就自然终止了
如果我们想中断一个线程,你需要手动的调用该线程的interrupt方法,该方法也仅仅是将线程的中断标识设成true,这里并没有真正的让线程停止运行,接着你需要自己写代码不断地检测当前线程的标识位,如果为true,表示别的线程要求这条线程中断,此时究竟该做什么需要你自己写代码实现。
每个线程对象中都有一个标识,用于表示线程是否被中断;该标识位为true表示中断,为false表示未中断;
通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。
eg.顾客在无烟餐厅中吸烟,服务员希望他别吸烟了,不是强行停止他吸烟,而是给他的标志位打为true,具体的停止吸烟还是要顾客自己停止。(体现了协商机制)
通过共享变量进行中断
通过AtomicBoolean(原子布尔型)
使用Thread.interrupt()静态方法进行中断
public class InterruptDemo {
static volatile boolean isStop = false;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (true){
if (isStop){
System.out.println(Thread.currentThread().getName()+"\t isStop被修改为true,程序终止");
break;
}
System.out.println("t1 ------hello volatile");//----------------------如果没停止,那就一直打印
}
},"t1").start();
Thread.sleep(20);
new Thread(()->{
isStop = true;
},"t2").start();
}
}
t1 ------hello volatile
t1 ------hello volatile
t1 ------hello volatile
t1 ------hello volatile
t1 isStop被修改为true,程序终止
public class InterruptDemo {
static volatile boolean isStop = false;
static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (true){
if (atomicBoolean.get()){
System.out.println(Thread.currentThread().getName()+"\t atomicBoolean被修改为true,程序终止");
break;
}
System.out.println("t1 ------hello volatile");//----------------------如果没停止,那就一直打印
}
},"t1").start();
Thread.sleep(20);
new Thread(()->{
atomicBoolean.set(true);
},"t2").start();
}
}
//t1 ------hello volatile
//t1 ------hello volatile
//t1 ------hello volatile
//t1 ------hello volatile
//t1 ------hello volatile
//t1 ------hello volatile
//t1 atomicBoolean被修改为true,程序终止
public class InterruptDemo {
static volatile boolean isStop = false;
static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + "\t isInterrupted()被修改为true,程序终止");
break;
}
System.out.println("t1 ------hello volatile");//----------------------如果没停止,那就一直打印
}
}, "t1");
t1.start();
Thread.sleep(20);
new Thread(()->{
t1.interrupt();
},"t2").start();
}
}
实例方法interrupt(),没有返回值
//Thread.java
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag----调用了interrupt0()方法
b.interrupt(this);
return;
}
}
interrupt0();
}
//Thread.java
/* Some private helper methods */
private native void setPriority0(int newPriority);
private native void stop0(Object o);
private native void suspend0();
private native void resume0();
private native void interrupt0(); //---------------------------调用了c底层
private native void setNativeName(String name);
实例方法isInterrupted,返回布尔值
//Thread.java
public boolean isInterrupted() {
return isInterrupted(false);
}
//Thread.java
private native boolean isInterrupted(boolean ClearInterrupted);//也调用了c底层
具体来说,当对一个线程,调用 interrupt() 时:
如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。所以, interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。
当线程调用sleep/wait/join等方法处于阻塞状态的时候,收到thread.interrupt(),就会抛出一个中断异常,InterruptedException
,当抛出这个异常的时候(无论是使用那种判断方式),当前线程的中断状态会被清除(也就是Thread类中那个特别设置的中断属性)
(中断不活动的线程不会产生任何影响,看下面案例)
public class InterruptDemo1 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 300; i++) {
System.out.println("---------" + i);
}
System.out.println("after t1.interrupt()---第2次----"+Thread.currentThread().isInterrupted());
}, "t1");
t1.start();
System.out.println("before t1.interrupt()----"+t1.isInterrupted());
t1.interrupt();
Thread.sleep(3);
System.out.println("after t1.interrupt()---第1次---"+t1.isInterrupted());
Thread.sleep(3000);
t1.interrupt();
System.out.println("after t1.interrupt()---第3次---"+t1.isInterrupted());
}
}
//before t1.interrupt()----false
//---------0
//---------1
//---------2
//---------3
//....
//---------136
//after t1.interrupt()---第1次---true ------此处中断标志位设置为了true,但是t1仍然在运行
//---------137
//---------298
//---------299
//after t1.interrupt()---第2次----true
//after t1.interrupt()---第3次---false//中断不活动的线程不会产生任何影响,线程结束后应该是自动变为了false
public class InterruptDem02 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (true){
if (Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"\t"+
"中断标志位:"+Thread.currentThread().isInterrupted()+"程序终止");
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// Thread.currentThread().interrupt(); 假如加了这个,程序可以终止,只会爆异常
e.printStackTrace();
}
System.out.println("-----hello InterruptDemo03");
}
}, "t1");
t1.start();
TimeUnit.MILLISECONDS.sleep(1);
new Thread(()-> t1.interrupt(),"t2").start();
}
}
/*java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.lsc.day04.InterruptDem02.lambda$main$0(InterruptDem02.java:23)
at java.lang.Thread.run(Thread.java:748)
-----hello InterruptDemo03
-----hello InterruptDemo03
-----hello InterruptDemo03
-----hello InterruptDemo03
-----hello InterruptDemo03
-----hello InterruptDemo03
*/
interrupt
方法),那么线程将立即退出被阻塞状态且会将中断标识位清除,会直接爆出异常(InterruptedException),但是线程并没有结束。
- 1 中断标志位 默认是false
- 2 t2 ----->t1发出了中断协商,t2调用t1.interrupt(),中断标志位true
- 3 中断标志位true,正常情况下,程序停止,-
- 4 中断标志位true,异常情况下,InterruptedException,将会把中断状态清除,并且将收到InterruptedException。中断标志位false导致无限循环。
- 5 在catch块中,需要再次给中断标志位设置为true,2次调用停止
public static boolean interrupted()
静态方法,Thread.interrupted();判断线程是否被中断,并清除当前中断状态这个方法做了两件事:
public class InterruptDemo3 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
System.out.println("-----1");
Thread.currentThread().interrupt();//中断标志位设置为true
System.out.println("-----2");
System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
}
}
main false
main false
-----1
-----2
main true
main false
看下源码,interrupted()对比isInterrupted()
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
private native boolean isInterrupted(boolean ClearInterrupted);
public boolean isInterrupted() {
return isInterrupted(false);
}
private native boolean isInterrupted(boolean ClearInterrupted);
他们在底层都调用了native方法isInterrupted。
只不过传入参数ClearInterrupted一个传参传了true,一个传了false。
静态方法interrupted() 中true表示清空当前中断状态。
实例方法isInterrupted 则不会。
阻塞原语
。核心就是park()和unpark()方法
park()方法是阻塞线程
unpark()方法是解除阻塞线程
在我们多线程开发的时候,有时候会有让一些线程先执行的,这些线程结束后,其他线程再继续执行,列如我们生活中打球,球场的每个人都是一个线程,那一个球员必须先传球给另一个球员,那么另一个球员才能投篮,这就是一个线程的一个动作执行完,另一个线程的动作才能执行
使用Object中的wait()
方法让线程等待,使用Object中的notify()
方法唤醒线程
使用JUC包中Condition的wait()
方法让线程等待,使用signal()
方法唤醒线程
LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程
public final void wait() throws InterruptedException {
wait(0);
}
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
痴汉方法,死等,线程进入阻塞态(WAITING),直到有其他线程调用notify方法唤醒
等待一段时间,若再该时间内线程被唤醒,则继续执行,若超过了相应时间还没有其他线程唤醒此线程,此线程不再等待,恢复执行等待方法做的事
等待方法做的事
调用wait方法的前提是获得这个对象的锁(synchronized对象锁,如果有多个线程取竞争这个锁,只有一个线程获得锁,其他线程会处于阻塞队列)
使当前执行代码的线程进行等待 . ( 把线程放到等待队列中 )
调用wait方法会释放锁
满足一定条件会重新尝试获得这个锁,被唤醒的之后不是立即恢复执行,而是进入阻塞队列,竞争锁
结束等待的三个方式
public final native void notify();
public final native void notifyAll();
public class ObjectWaitAndNotify {
public static void main(String[] args) {
Object objectLock = new Object();
new Thread(()->{
synchronized (objectLock){
System.out.println(Thread.currentThread().getName()+"\t ---- come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"\t"+"---被唤醒了");
},"t1").start();
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
synchronized (objectLock){
objectLock.notify();
System.out.println(Thread.currentThread().getName()+"\t ---发出通知");
}
},"t2").start();
}
}
异常1—去掉synchronized
public class ObjectWaitAndNotify {
public static void main(String[] args) {
Object objectLock = new Object();
new Thread(()->{
// synchronized (objectLock){
System.out.println(Thread.currentThread().getName()+"\t ---- come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
// }
System.out.println(Thread.currentThread().getName()+"\t"+"---被唤醒了");
},"t1").start();
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
// synchronized (objectLock){
objectLock.notify();
System.out.println(Thread.currentThread().getName()+"\t ---发出通知");
// }
},"t2").start();
}
}
t1 ---- come in
Exception in thread "t1" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.lsc.day04.ObjectWaitAndNotify.lambda$main$0(ObjectWaitAndNotify.java:20)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "t2" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.lsc.day04.ObjectWaitAndNotify.lambda$main$1(ObjectWaitAndNotify.java:31)
at java.lang.Thread.run(Thread.java:748)
异常2—把notify和wait的执行顺序对换
public class ObjectWaitAndNotify{
public static void main(String[] args){
Object objectLock = new Object();
new Thread(() -> {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (objectLock) {
System.out.println(Thread.currentThread().getName()+"\t ---- come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"\t"+"---被唤醒了");
},"t1").start();
new Thread(() -> {
synchronized (objectLock) {
objectLock.notify();//这个先执行了
System.out.println(Thread.currentThread().getName()+"\t ---发出通知");
}
},"t2").start();
}
}
//一直处于循环中
小总结
背后工作原理解析
wait和sleep的区别
为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在 Object 类里
为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用
public class ConditionAwaitAndSignal {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t-----come in");
condition.await();
System.out.println(Thread.currentThread().getName()+"\t -----被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},"t1").start();
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
lock.lock();
try {
condition.signal();
}catch (Exception e){
e.printStackTrace();
}
finally {
lock.unlock();
}
System.out.println(Thread.currentThread().getName()+"\t"+"我要进行唤醒");
},"t2").start();
}
}
异常1原理同上
异常2 原理同上
小总结
await和notify类似于上面wait和notify
Condition中的线程等待和唤醒方法,需要先获取锁
一定要先await后signal,不能反了
总结
线程先要获得并持有锁,必须在锁块(synchronized或lock)中
必须要先等待后唤醒,线程才能够被唤醒
是什么
官网解释
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
。
LockSupport类使用了一种名为Permit(许可) 的概念来做到阻塞和唤醒线程的功能, 每个线程都有一个许可(permit),
permit(许可)只有两个值1和0,
可以把许可看成是一种(0,1)信号量(Semaphore),但与 Semaphore 不同的是,许可的累加上限是1。
调用LockSupport.park()时,发现它调用了unsafe类,并且默认传了一个0
public static void park() {
UNSAFE.park(false, 0L);
}
public native void park(boolean var1, long var2);
调用LockSupport.unpark();时,也调用了unsafe类
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
public native void unpark(Object var1);
public class LockSupportParkAndUnPark {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t----------come in");
LockSupport.park();
System.out.println(Thread.currentThread().getName()+"\t----------被唤醒了");
}, "t1");
t1.start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t----------come in");
LockSupport.unpark(t1);
System.out.println(Thread.currentThread().getName()+"\t----------我来唤醒了");
}, "t2").start();
}
}
t1 ----------come in
t2 ----------come in
t2 ----------我来唤醒了
t1 ----------被唤醒了
之前错误的先唤醒后等待,LockSupport照样支持
public class LockSupportParkAndUnPark {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t----------come in");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.park();
System.out.println(Thread.currentThread().getName()+"\t----------被唤醒了");
}, "t1");
t1.start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t----------come in");
LockSupport.unpark(t1);
System.out.println(Thread.currentThread().getName()+"\t----------我来唤醒了");
}, "t2").start();
}
}
t1 ----------come in
t2 ----------come in
t2 ----------我来唤醒了
t1 ----------被唤醒了
sleep方法3秒后醒来,执行park无效,没有阻塞效果,解释如下。先执行了unpark(t1)导致上面的park方法形同虚设无效,时间是一样的
public class LockSupportDemo
{
public static void main(String[] args) {
Thread t1 = new Thread(()->{
try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"\t----------come in"+"\t"+System.currentTimeMillis());
LockSupport.park();
LockSupport.park();
System.out.println(Thread.currentThread().getName()+"\t----------被唤醒了"+"\t"+System.currentTimeMillis());
},"t1");
t1.start();
new Thread(()->{
LockSupport.unpark(t1);
LockSupport.unpark(t1);
System.out.println(Thread.currentThread().getName()+"\t-----发出通知,去唤醒t1");
},"t2").start();
}
}
//t2 -----发出通知,去唤醒t1
//t1 ----------come in 1654750970677--------------------卡在这里了
小总结
Lock Support是用来创建锁和其他同步类的基本线程阻塞原语。
Lock Support是一个线程阻塞工具类, 所有的方法都是静态方法, 可以让线程在任意位置阻塞, 阻塞之后也有对应的唤醒方法。归根结底, Lock Support调用的Unsafe中的native代码。
Lock Support提供park() 和unpark() 方法实现阻塞线程和解除线程阻塞的过程
Lock Support和每个使用它的线程都有一个许可(permit) 关联。
每个线程都有一个相关的permit, permit最多只有一个, 重复调用unpark也不会积累凭证。
面试题
为什么可以突破wait/notify的原有调用顺序?
为什么唤醒两次后阻塞两次,但最终结果还会阻塞线程?