系列文章目录
第一章 java JUC并发编程 Future: link
第二章 java JUC并发编程 多线程锁: link
第三章 java JUC并发编程 中断机制: link
第四章 java JUC并发编程 java内存模型JMM: link
第五章 java JUC并发编程 volatile与JMM: link
第六章 java JUC并发编程 CAS: link
第七章 java JUC并发编程 原子操作类增强: link
第八章 java JUC并发编程 ThreadLocal: link
第九章 java JUC并发编程 对象内存布局与对象头: link
第十章 java JUC并发编程 Synchronized与锁升级: link
第十一章 java JUC并发编程 AbstractQueuedSynchronizer之AQS: link
1 LockSupport前言
interrupt:中断此线程
interrupted:测试当前线程是否已被中断
isInterrupted:测试此线程是否已被中断
什么是中断机制
1.1 如何停止中断运行中的线程
1.1.1 通过volatile变量实现
package com.util.interrup;
import java.util.concurrent.TimeUnit;
public class InterrupDemo {
static volatile boolean isStop = false;
public static void main(String[] args) {
new Thread(()->{
while (true){
if(isStop){
System.out.println(Thread.currentThread().getName()+"isStop被修改为true,程序停止");
break;
}
System.out.println("----hello");
}
},"t1").start();
try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
new Thread(()->{
isStop=true;
},"t2").start();
}
}
1.1.2 通过AtomicBoolean实现
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class InterrupDemo {
static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
public static void main(String[] args) {
new Thread(()->{
while (true){
if(atomicBoolean.get()){
System.out.println(Thread.currentThread().getName()+"isStop被修改为true,程序停止");
break;
}
System.out.println("----hello");
}
},"t1").start();
try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
new Thread(()->{
atomicBoolean.set(true);
},"t2").start();
}
}
1.1.3 通过Thread类自带的中断api实现
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.TimeUnit;
public class InterrupDemo {
public static void main(String[] args) {
Thread t1 = new Thread(()->{
while (true){
if(Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"isInterrupted被修改为true,程序停止");
break;
}
System.out.println("t1 ---- hello interrupt api");
}
},"t1");
t1.start();
try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
//t2向t1发出协商,将t1的中断标志位设为true希望t1停下来
Thread t2 = new Thread(()->{
t1.interrupt();
},"t2");
t2.start();
//t1.interrupt();
}
}
1.1.3.1 interrupt()源码分析
底层调用的是interrupt0() native的方法
1.1.3.2 isInterrupted源码分析
返回线程是否被中断过,中断的状态是否被重置,取决于当前ClearInterrupted的入参
1.2当前线程的中断标识为true,是不是线程就立刻停止
不会的
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.TimeUnit;
public class InterrupDemo2 {
public static void main(String[] args) {
//实例方法interrupt(),仅仅是设置线程的中断状态设置为true,不会停止线程。
Thread t1 = new Thread(()->{
for (int i = 0; i < 300; i++) {
System.out.println("----i="+i);
}
},"t1");
t1.start();
System.out.println("t1线程默认的中断标志02:"+Thread.currentThread().isInterrupted());//false
try {TimeUnit.MILLISECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
t1.interrupt();//true
System.out.println("t1线程调用interrupt()后的中断标志01:"+t1.isInterrupted());//true
try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("t1线程调用interrupt()后的中断标志03:"+t1.isInterrupted());//false:2秒t1线程结束了不活动的线程不会产生任何影响
}
}
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.TimeUnit;
public class InterrupDemo3 {
public static void main(String[] args) {
/**
* 1 中断标志位,默认false
* 2 t2--->t1发出了中断协商,t2调用t1.interrupt(),中断标志位true
* 3 中断标志位true,正常情况,程序停止
* 4 中断标志位true,异常情况,InterruptedException,将会把中断状态被清除,并且将收到InterruptedException.中断标志位false
* 导致无线循环
* 5 在catch块中,需要再次给中断标志位设置为true,2次调用停止程序才ok
*/
Thread t1 = new Thread(()->{
while (true){
if(Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"线程----isInterrupted()=true,自己退出了");
break;
}
try {
Thread.sleep(200);
}catch (InterruptedException e) {
Thread.currentThread().interrupt();//没有它,程序不会停止,中断不打断
e.printStackTrace();
}
System.out.println("----hello InterruptDemo3");
}
},"t1");
t1.start();
try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e) {e.printStackTrace();}
t1.interrupt();
}
}
中断只是一种协商机制,修改中断标志位而已,不是立刻stop打断
1.3 静态方法Thread.interrupted()的理解
说明
package com.atguigu.springcloud.util.interrup;
public class InterrupDemo4 {
public static void main(String[] args) {
//测试当前线程是否被中断(检查中断标志),返回一个boolean并清楚中断状态。
//第二次调用时中断状态已经被清除,将返回一个False.
System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());//false main线程的中断状态false
System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());//false main线程的中断状态false
System.out.println("----1");
Thread.currentThread().interrupt();//中断标志设置为true main线程的中断状态设置为true
System.out.println("----2");
System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());//true 会把当前的中断状态清零并重新设置为false
System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());//false main线程的中断状态false
}
}
interrupt:是否需要清理中断标志位传的是true清理
isInterrupted:是否需要清理中断标志位传的是false不清理
总结
2 LockSuppor
2.1 3种让线程等待和唤醒的方法
2.1.1 方式1:使用Object中的wait()方法让线程等待,使用Object中的notify方法唤醒线程
package com.ge.healthcare.cn.apm.gateway.util;
public class LockSupportDemo {
static Object objectLock = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (objectLock){
System.out.println(Thread.currentThread().getName()+" --------come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" -----------------被唤醒");
}
},"A").start();
new Thread(()->{
synchronized (objectLock){
objectLock.notify();
System.out.println(Thread.currentThread().getName()+" ------------通知");
}
},"B").start();
}
}
2.1.1.1 异常情况1
package com.atguigu.springcloud.util;
public class LockSupportDemo {
static Object objectLock = new Object();
public static void main(String[] args) {
new Thread(()->{
// synchronized (objectLock){
System.out.println(Thread.currentThread().getName()+" --------come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" -----------------被唤醒");
// }
},"A").start();
new Thread(()->{
// synchronized (objectLock){
objectLock.notify();
System.out.println(Thread.currentThread().getName()+" ------------通知");
// }
},"B").start();
}
}
如果要使用wait和notify必须将它包在synchronized锁块之间
2.1.1.2 异常情况2
先notify再wait,程序无法执行,无法唤醒
package com.atguigu.springcloud.util;
import java.util.concurrent.TimeUnit;
public class LockSupportDemo {
static Object objectLock = new Object();
public static void main(String[] args) {
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e) {e.printStackTrace();}
synchronized (objectLock){
System.out.println(Thread.currentThread().getName()+" --------come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" -----------------被唤醒");
}
},"A").start();
new Thread(()->{
synchronized (objectLock){
objectLock.notify();
System.out.println(Thread.currentThread().getName()+" ------------通知");
}
},"B").start();
}
}
总结:wait和notify方法必须要在同步块或者方法里面,且成对出现使用
先wait后notify才行
2.1.2 方式2:使用JUC包中的Condition的await方法让线程等待,使用signal方法唤醒线程
package com.atguigu.springcloud.util;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockSupportDemo {
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main(String[] args) {
new Thread(()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"----------come in");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"----------被唤醒");
} finally {
lock.unlock();
}
},"A").start();
new Thread(()->{
lock.lock();
try {
condition.signal();
System.out.println(Thread.currentThread().getName()+"----------通知");
} finally {
lock.unlock();
}
},"B").start();
}
}
2.1.2.1 异常情况1
注掉lock,unlock
同2.1.1.1
2.1.2.2 异常情况2
先signal后await
同2.1.1.2
总结:Condition中的线程等待和唤醒方法,需要先获取锁
一定要先await后signal,不要反了。
2.1.3 方式3: LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程
通过park和unpark(thread)方法来实现阻塞和唤醒线程的操作
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit),但与Semaphore不同的是,许可的累加上限是1.
0L:如果没有通行证就永远不放行
package com.atguigu.springcloud.util;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
public class LockSupportDemo {
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
Thread a = new Thread(()->{
System.out.println(Thread.currentThread().getName()+"--------------come in");
LockSupport.park();//被阻塞....等待通知放行,它要通过时需要许可证
System.out.println(Thread.currentThread().getName()+"--------------被唤醒");
},"a");
a.start();
try {
TimeUnit.SECONDS.sleep(3L);} catch (InterruptedException e) { e.printStackTrace();}
Thread b = new Thread(()->{
LockSupport.unpark(a);
System.out.println(Thread.currentThread().getName()+"--------------通知了");
},"b");
b.start();
}
}
如果b线程先发通知,a线程后被阻塞运行结果:正常
并且无锁块的要求
原因是先执行了unpark(a)导致上面的park方法形同虚设无效。
package com.atguigu.springcloud.util;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
public class LockSupportDemo {
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
Thread a = new Thread(()->{
try {TimeUnit.SECONDS.sleep(3L);} catch (InterruptedException e) { e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"--------------come in");
LockSupport.park();//被阻塞....等待通知放行,它要通过时需要许可证
System.out.println(Thread.currentThread().getName()+"--------------被唤醒");
},"a");
a.start();
Thread b = new Thread(()->{
LockSupport.unpark(a);
System.out.println(Thread.currentThread().getName()+"--------------通知了");
},"b");
b.start();
}
}
2.1.3.1 问题多个park和unpark会阻塞么:会
因为许可证不会累计,不要出现多对多
package com.atguigu.springcloud.util;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
public class LockSupportDemo {
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
Thread a = new Thread(()->{
try {TimeUnit.SECONDS.sleep(3L);} catch (InterruptedException e) { e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"--------------come in");
LockSupport.park();//被阻塞....等待通知放行,它要通过时需要许可证
LockSupport.park();//被阻塞....等待通知放行,它要通过时需要许可证
System.out.println(Thread.currentThread().getName()+"--------------被唤醒");
},"a");
a.start();
Thread b = new Thread(()->{
LockSupport.unpark(a);
LockSupport.unpark(a);
System.out.println(Thread.currentThread().getName()+"--------------通知了");
},"b");
b.start();
}
}
2.1.3.2 总结