长春网站建设开发的有哪些,金方时代做网站怎么样,不良广告入口,英文网站建设价格目录
LockSupport与线程中断
线程中断机制
什么是中断机制#xff1f;
与中断相关的3个API
如何停止中断运行中的线程#xff1f;
当前线程的中断标识为true#xff0c;是不是线程就会立刻停止#xff1f;
如何理解静态方法Thread.interrupted()
LockSupport是什么…目录
LockSupport与线程中断
线程中断机制
什么是中断机制
与中断相关的3个API
如何停止中断运行中的线程
当前线程的中断标识为true是不是线程就会立刻停止
如何理解静态方法Thread.interrupted()
LockSupport是什么
线程等待和唤醒机制
3种让线程等待唤醒的方法
Object类中的wait()和notify()方法实现线程的等待和唤醒
Condition接口中的await()后signal()方法实现线程的等待和唤醒
上述两个对象Object和Condition使用的限制条件
LockSupport类中的park()等待和unpack()唤醒
总结 LockSupport与线程中断
线程中断机制
什么是中断机制
首先一个线程不应该由其他线程强制中断或停止而是应该由线程自己自行停止自己来决定自己的命运所以Thread的stop()、suspend()、resume()都已经废弃了
其次在Java中没有办法立即停止一条线程然而停止线程又显得那么重要比如需要取消一个耗时/错误操作。因此Java提供了一种用于停止线程的协商机制——中断也即中断标识协商机制。
中断只是一种协作协商机制Java没有给中断增加任何语法中断的过程完成完全要求程序员自己实现。若要中断一个线程需要手动调用该线程的interrupt()方法该方法也仅仅是将线程对象的中断标识设成true接着按自己的需要写代码不断监测当前线程的标识位如果为true表示别的线程请求这条线程中断此时究竟该做什么需要自己写代码实现。
每个线程对象中都有一个中断标识位用于表示线程是否被中断该标识位为true标识中断为false表示未中断。通过调用线程对象的interrupt方法将该线程的标识位设为true可以在别的线程中带调用也可以在自己的线程中调用。
与中断相关的3个API
public void interrupt()设置线程的中断状态为true发起一个协商而不会立刻停止线程public static boolean isInterrupted()通过检查中断标识位判断线程是否被中断并清除当前中断状态返回当前线程的中断状态重置中断状态为falsepublic boolean isInterrupted()通过检查中断标识位判断当前线程是否被中断
如何停止中断运行中的线程
通过volatile变量实现线程间的可见性通过AtomicBoolean实现
执行结果如下图 通过Thread类自带的中断API实例方法实现 在需要中断的线程中不断监听中断状态一旦发生中断就执行相应的中断处理业务逻辑stop线程 interrupt() 中断线程 我们可以看到interrupt0()是一个native方法 除非线程正在中断始终允许否则将带调用此线程的checkAccess()方法可能还会导致抛出SecurityException public class InterruptDemo {static volatile boolean isStop false;public static void main(String[] args) {Thread tA new Thread(() - {while (true) {if (isStop) {System.out.printf(Thread.currentThread().getName() isStop 被修改为true);break;}System.out.println(--------------------);}}, tA);tA.start();try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() - {isStop true;}, B).start();}
}
执行结果如下图 public class InterruptDemo {static AtomicBoolean atomicBoolean new AtomicBoolean(false);public static void main(String[] args) {Thread tA new Thread(() - {while (true) {if (atomicBoolean.get()) {System.out.printf(Thread.currentThread().getName() atomicBoolean 被修改为true);break;}System.out.println(--------------------);}}, tA);tA.start();try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() - {atomicBoolean.set(true);}, B).start();}
}
package com.aqin.juc;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;/*** Description* Author aqin1012 AQin.* Date 10/9/23 11:29 AM* Version 1.0*/
public class InterruptDemo {public static void main(String[] args) {Thread tA new Thread(() - {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.printf(Thread.currentThread().getName() isInterrupted() 被修改为true程序停止);break;}System.out.println(--------------------);}}, tA);tA.start();try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() - {tA.interrupt();}, B).start();}
}
执行效果如下图 当前线程的中断标识为true是不是线程就会立刻停止
不会具体来说对一个线程调用interrupt()时如果线程处于正常活动状态那么会将该线程的中断标志设置为true仅此而已被设置中断标志的线程将继续正常运行不受影响。所以interrupt()并不是真正的中断线程需要被调用的线程自己进行配合才行。如果线程处于被阻塞状态如sleep、wait、join等状态在别的线程中调用当前线程对象的interrupt()方法时那么线程将会立即退出被阻塞状态并抛出一个InterruptedException异常。
如何理解静态方法Thread.interrupted() 这个方法是判断线程是否中断并清除当前中断状态的简单来说就是“复位”。
主要做了两件事
返回当前线程的中断状态测试当前线程是否已经被中断将当前线程的中断状态清零并重新设置为false清除线程的中断状态
那么问题来了这个静态方法interrupted()跟实例方法isInterrupted()有什么区别呢
我们举个简单的例子对比下执行结果
静态方法interrupted()
执行结果如下 实例方法isInterrupted()
执行结果如下 来看它俩的源码对比 可以看到其实它们调用的是同一个方法只不过在对于是否需要清理这个参数的传递上静态方法传递的是true而实例方法传递的是false简单来讲就是多了一步“复位”操作。因此才会出现上面两段示例代码执行的结果不一致在当前线程未执行中断方法interrupt()时当前线程的中断标识位本身就是初始值false因此连续调用两次静态方法或者实例方法的执行结果都是false当当前线程执行了中断方法interrupt()时第一次调用静态方法和实例方法的返回值同样都是true但是此时静态方法多做了一步“复位”操作把当前线程的中断标识位重置回了初始值false而实例方法则没有这步操作因此当第二次调用时实例方法的示例中当前线程的中断标识位仍然是true因此仍然返回true而静态方法的示例代码中当前线程的中断标识位已经被重置回了false于是就返回了false。
LockSupport是什么
LockSupport是java.util.concurrent.locks中的一个类用于创建锁和其他同步类的基本线程阻塞原语。
线程等待和唤醒机制
3种让线程等待唤醒的方法
使用Object中的wait()方法让线程等待使用notify()方法唤醒线程使用JUC包中的Condition的await()方法让线程等待使用signal()方法唤醒线程使用LockSupport类中的park()和unpark()方法阻塞当前线程以及唤醒指定被阻塞的线程
Object类中的wait()和notify()方法实现线程的等待和唤醒
示例代码
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() 进入---);try {objectLock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() 被唤醒^ ^);}}, A).start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() - {synchronized (objectLock) {objectLock.notify();}System.out.println(Thread.currentThread().getName() 发出唤醒通知(∇)/);}, B).start();
}
执行结果如下 问题
必须要先持有锁否则会报错 必须先wait()再notify()否则会卡死 Condition接口中的await()后signal()方法实现线程的等待和唤醒
示例代码
public static void main(String[] args) {Lock lock new ReentrantLock();Condition condition lock.newCondition();new Thread(() - {try { TimeUnit.SECONDS.sleep(1); } catch ( InterruptedException e) { e.printStackTrace();}lock.lock();System.out.println(Thread.currentThread().getName() 进入---);try {condition.await();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}System.out.println(Thread.currentThread().getName() 被唤醒^ ^);}, A).start();try { TimeUnit.SECONDS.sleep(1); } catch ( InterruptedException e) { e.printStackTrace();}new Thread(() - {lock.lock();try {condition.signal();} finally {lock.unlock();}System.out.println(Thread.currentThread().getName() 发出唤醒通知(∇)/);}, B).start();}
执行结果如下 问题
必须要先持有锁否则会报错 在lock、unlock对里面才能正确调用condition中线程等待和唤醒的方法。
必须先await()再signal()否则会卡死 上述两个对象Object和Condition使用的限制条件
线程先要获得并持有锁必须在锁块synchronized/lock中必须要先等待后唤醒线程才能够被唤醒
LockSupport类中的park()等待和unpack()唤醒
LockSupport类使用了一种名为Permit许可的概念来做到阻塞和唤醒线程的功能每一个线程都有一个Permit许可但与Semaphore不同的是许可的累加上限是1最多一个许可。
使用示例
public static void main(String[] args) {Thread A new Thread(() - {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() 进入---);LockSupport.park();System.out.println(Thread.currentThread().getName() 被唤醒^ ^);}, A);A.start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() - {LockSupport.unpark(A);System.out.println(Thread.currentThread().getName() 发出唤醒通知(∇)/);}, B).start();
}
执行结果如下 可以看到使用LockSupport进行线程阻塞/唤醒不需要在锁对块synchronized/lock中所以上面Object类和Condition接口的第一个问题解决了然后我们看看第二个问题加锁/解锁的先后顺序。 如上图可以看出park()和unpack()执行的先后顺序不会影响结果。
总结
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport是一个线程阻塞工具所有的方法都是静态方法可以让线程在任意位置阻塞阻塞后也有对应的唤醒方法归根结底LockSupport调用的是Unsafe中的native代码。 LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程每个使用LockSupport的线程都一个Permit许可与LockSupport相关联每一个线程都都一个相关的Permit并且最多一个重复调用unpark()也不会积累Permit。
换句话讲线程阻塞需要消耗凭证并且这个凭证最多1个
当调用park()方法时
如果有Permit会消耗这个Permit然后正常退出如果没有Permit会阻塞当前线程等待获取凭证
当调用unpark()方法时
会增加一个Permit调用多次也只会有一个Permit不累加
最后我们思考2个问题
为什么LockSupport提供park()和unpark()方法可以突破Object和Condition的调用先后顺序的限制 因为调用unpark()会获得1个Permit之后再调用park()会消耗这个Permitpark()和unpark()方法的执行顺序不会影响线程的唤醒即便先发放了Permit仍然不会阻塞线程。唤醒两次调用两次park()方法后阻塞两次调用两次unpark)方法会发生什么情况 线程会被阻塞因为Permit不会累加即使调用了两次unpark()仍然只会有一个Permit后面接着的调用两次park()在调用第一次park()时就会把这个Permit消耗掉第二次调用时就没有Permit了因而会被阻塞。
搞定(∇)/