🔥(大廠必備)廠長熬夜爆肝萬字之多執行緒高并發JUC編程(二)?學妹已收藏
關于作者
- 作者介紹
🍓 博客主頁:作者主頁
🍓 簡介:JAVA領域優質創作者🥇、一名在校大三學生🎓、在校期間參加各種省賽、國賽,斬獲一系列榮譽🏆,
🍓 關注我:關注我學習資料、檔案下載統統都有,每日定時更新文章,勵志做一名JAVA資深程式猿👨?💻,
JUC學習
文章目錄
- 🔥(大廠必備)廠長熬夜爆肝萬字之多執行緒高并發JUC編程(二)?學妹已收藏
- 關于作者
- JUC學習
- 15、異步回呼
- (1)沒有回傳值的runAsync異步回呼
- (2)有回傳值的異步回呼supplyAsync
- 16、JMM(Java Memory Model )
- 1)我們先了解一下什么JMM?
- 17、volatile
- 1)保證可見性
- 2)不保證原子性
- 3)禁止指令重排
- 4)總結
- 🥰面試題:在哪里用這個記憶體屏障用得最多呢?
- 18、單例模式
- 1)餓漢式
- 2)DCL懶漢式
- 3)靜態內部類
- 4)列舉
- 19、深入理解CAS
- 1)什么是CAS?
- 2)總結
- 20、原子參考
- 21、各種鎖的理解
- 1)公平鎖,非公平鎖
- 2)可重入鎖
- 3)自旋鎖
- 4)死鎖
- 🤡我們如何去解決死鎖的問題?
15、異步回呼
Future 設計的初衷:對將來的某個事件結果進行建模!
其實就是前端 —》發送ajax異步請求給后端

但是我們平時都使用CompletableFuture
(1)沒有回傳值的runAsync異步回呼
package com.zmz.Async;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @ProjectName: Juc
* @Package: com.zmz.Async
* @ClassName: runAsync
* @Author: 張晟睿
* @Date: 2021/10/11 18:58
* @Version: 1.0
*/
public class runAsync {
public static void main(String[] args) throws ExecutionException, InterruptedException
{
// 發起 一個 請求
System.out.println(System.currentTimeMillis());
System.out.println("---------------------");
CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
//發起一個異步任務
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+".....");
});
System.out.println(System.currentTimeMillis());
System.out.println("------------------------------");
//輸出執行結果
System.out.println(future.get()); //獲取執行結果
}
}
(2)有回傳值的異步回呼supplyAsync
package com.zmz.Async;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @ProjectName: Juc
* @Package: com.zmz.Async
* @ClassName: supplyAsync
* @Author: 張晟睿
* @Date: 2021/10/11 19:09
* @Version: 1.0
*/
public class supplyAsync {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//有回傳值的異步回呼
CompletableFuture<Integer> completableFuture=CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(2);
int i=1/0;
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1024;
});
System.out.println(completableFuture.whenComplete((t, u) -> {
/*我們可以看到whenComplete以上的程式有兩個引數,一個是t 一個是u
T:是代表的 正常回傳的結果;
U:是代表的 拋出例外的錯誤資訊;
如果發生了例外,get可以獲取到exceptionally回傳的值;
*/
//success 回呼
System.out.println("t=>" + t); //正常的回傳結果
System.out.println("u=>" + u); //拋出例外的 錯誤資訊
}).exceptionally((e) -> {
//error回呼
System.out.println(e.getMessage());
return 404;
}).get());
}
}
16、JMM(Java Memory Model )
1)我們先了解一下什么JMM?
JMM:JAVA記憶體模型,不存在的東西,抽象的,是一個概念,也是一個約定!
關于JMM的一些同步的約定:
- 執行緒解鎖前,必須把共享變數立刻刷回主存
- 執行緒加鎖前,必須讀取主存中的最新值到作業記憶體中
- 加鎖和解鎖是同一把鎖
執行緒中分為 作業記憶體、主記憶體,
八種操作:
| 名稱 | 描述 |
|---|---|
| Read(讀取) | 作用于主記憶體變數,它把一個變數的值從主記憶體傳輸到執行緒的作業記憶體中,以便隨后的load動作使用 |
| load(載入) | 作用于作業記憶體的變數,它把read操作從主存中變數放入作業記憶體中 |
| Use(使用) | 作用于作業記憶體中的變數,它把作業記憶體中的變數傳輸給執行引擎,每當虛擬機遇到一個需要使用到變數的值,就會使用到這個指令 |
| assign(賦值) | 作用于作業記憶體中的變數,它把一個從執行引擎中接受到的值放入作業記憶體的變數副本中 |
| store(存盤) | 作用于主記憶體中的變數,它把一個從作業記憶體中一個變數的值傳送到主記憶體中,以便后續的write使用 |
| write(寫入) | 作用于主記憶體中的變數,它把store操作從作業記憶體中得到的變數的值放入主記憶體的變數中 |
| lock(鎖定) | 作用于主記憶體的變數,把一個變數標識為執行緒獨占狀態 |
| unlock(解鎖) | 作用于主記憶體的變數,它把一個處于鎖定狀態的變數釋放出來,釋放后的變數才可以被其他執行緒鎖定 |


對于八種操作給了相應的規定:
- 不允許read和load、store和write操作之一單獨出現,即使用了read必須load,使用了store必須write
- 不允許執行緒丟棄他最近的assign操作,即作業變數的資料改變了之后,必須告知主存
- 不允許一個執行緒將沒有assign的資料從作業記憶體同步回主記憶體
- 一個新的變數必須在主記憶體中誕生,不允許作業記憶體直接使用一個未被初始化的變數,就是對變數實施use、store操作之前,必須經過assign和load操作
- 一個變數同一時間只有一個執行緒能對其進行lock,多次lock后,必須執行相同次數的unlock才能解鎖
- 如果對一個變數進行lock操作,會清空所有作業記憶體中此變數的值,在執行引擎使用這個變數前,必須重新load或assign操作初始化變數的值
- 如果一個變數沒有被lock,就不能對其進行unlock操作,也不能unlock一個被其他執行緒鎖住的變數
- 對一個變數進行unlock操作之前,必須把此變數同步回主記憶體

遇到問題:程式不知道主存中的值已經被修改過了!
17、volatile
1)保證可見性
package com.zmz.JMM;
import java.util.concurrent.TimeUnit;
/**
* @ProjectName: Juc
* @Package: com.zmz.JMM
* @ClassName: JMMdemo01
* @Author: 張晟睿
* @Date: 2021/10/11 20:43
* @Version: 1.0
*/
public class JMMdemo01 {
// 如果不加volatile 程式會死回圈
// 加了volatile是可以保證可見性的
private volatile static Integer number = 0;
public static void main(String[] args) {
//main執行緒
//子執行緒1
new Thread(()->{
while (number==0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//子執行緒2
new Thread(()->{
while (number==0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
number=1;
System.out.println(number);
}
}
2)不保證原子性
原子性:意思就是說不可分割,舉一個例子就是說執行緒A在執行任務的時候,不能被打擾的,也不能被分割的,要么同時成功,要么同時失敗,
package com.zmz.JMM;
/**
* @ProjectName: Juc
* @Package: com.zmz.JMM
* @ClassName: JMMdemo02
* @Author: 張晟睿
* @Date: 2021/10/11 20:46
* @Version: 1.0
*/
public class JMMdemo02 {
private static volatile int num = 0;
public static void add(){
num++;
//++ 不是一個原子性操作,是2個~3個操作
}
public static void main(String[] args) {
//理論上number === 20000
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int j = 1; j <= 1000 ; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){
//main gc
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+",num="+num);
}
}
使用原子類

package com.zmz.JMM;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ProjectName: Juc
* @Package: com.zmz.JMM
* @ClassName: JMMdemo03
* @Author: 張晟睿
* @Date: 2021/10/11 21:05
* @Version: 1.0
*/
public class JMMdemo03 {
private static volatile AtomicInteger number = new AtomicInteger();
public static void add(){
// number++;
number.incrementAndGet(); //底層是CAS保證的原子性
}
public static void main(String[] args) {
//理論上number === 20000
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int j = 1; j <= 1000 ; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){
//main gc
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+",num="+number);
}
}
這些類的底層都直接和作業系統掛鉤!是在記憶體中修改值,
3)禁止指令重排
什么是指令重排?
我們寫的程式,計算機并不是按照我們自己寫的那樣去執行的
源代碼–>編譯器優化重排–>指令并行也可能會重排–>記憶體系統也會重排–>執行
處理器在進行指令重排的時候,會考慮資料之間的依賴性!
int x=2; //1
int y=4; //2
x=x+10; //3
y=x*x; //4
//我們期望的執行順序是 1_2_3_4 可能執行的順序會變成3124 1423
//可不可能是 4123? 不可能的
1234567
可能造成的影響結果:前提:a b x y這四個值 默認都是0
| 執行緒A | 執行緒B |
|---|---|
| x=a | y=b |
| b=1 | a=2 |
正常的結果: x = 0; y =0
| 執行緒A | 執行緒B |
|---|---|
| b=1 | a=2 |
| x=a | y=b |
可能在執行緒A中會出現,先執行b=1,然后再執行x=a
在B執行緒中可能會出現,先執行a=2,然后執行y=b
那么就有可能結果如下:x=4; y=2
volatile可以避免指令重排:
volatile中會加一道記憶體的屏障,這個記憶體屏障可以保證在這個屏障中的指令順序,
記憶體屏障:CPU指令,作用:保證特定的操作的執行順序;可以保證某些變數的記憶體可見性(利用這些特性,就可以保證volatile實作的可見性)

4)總結
- 由于記憶體屏障,可以保證避免指令重排的現象產生
- 不能保證原子性
- volatile可以保證可見性
🥰面試題:在哪里用這個記憶體屏障用得最多呢?
單例模式
18、單例模式
1)餓漢式
package single;
//餓漢式單例模式
@SuppressWarnings("all")
public class Hungry {
private byte[] date1= new byte[1024*1024];
private byte[] date2= new byte[1024*1024];
private byte[] date3= new byte[1024*1024];
private byte[] date4= new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
2)DCL懶漢式
package single;
import java.lang.reflect.Constructor;
//懶漢式
public class LazyMan {
private LazyMan(){
synchronized(LazyMan.class){
throw new RuntimeException("不要試圖使用反射破壞例外");
}
// System.out.println(Thread.currentThread().getName()+"ok");
}
private static LazyMan lazyMan;
//雙重檢測鎖
public static LazyMan getInstance(){
if (lazyMan==null) {
synchronized (LazyMan.class) {
if (lazyMan==null) {
lazyMan = new LazyMan();
//不是原子性操作
//1.分配記憶體空間
//2.執行構造方法,初始化物件
//3.把這個物件指向空間
}
}
}
return lazyMan;
}
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
for (int i = 0; i < 2; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
3)靜態內部類
package single;
public class Holder {
private Holder(){}
private static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass {
private static final Holder HOLDER = new Holder();
}
}
單例不安全, 主要的原因是因為反射,
4)列舉
package com.zmz.Singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @ProjectName: Juc
* @Package: com.zmz.Singleton
* @ClassName: EnumSingle
* @Author: 張晟睿
* @Date: 2021/10/12 20:06
* @Version: 1.0
*/
//enum 是什么? enum本身就是一個Class 類
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
//java.lang.NoSuchMethodException: com.ogj.single.EnumSingle.<init>()
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}

列舉型別的最終反編譯原始碼:
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/ogj/single/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}
19、深入理解CAS
1)什么是CAS?
CAS(Compare And Swap比較并替換),包含三個值當前記憶體值(V)、預期原來的值(A)以及期待更新的值(B),這里我們先做簡單的介紹后期會出一期博文進行單獨介紹的,
package com.zmz.CAS;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ProjectName: Juc
* @Package: com.zmz.CAS
* @ClassName: CasDemo01
* @Author: 張晟睿
* @Date: 2021/10/12 20:15
* @Version: 1.0
*/
public class CasDemo01 {
//CAS : compareAndSet 比較并交換
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//boolean compareAndSet(int expect, int update)
//期望值、更新值
//如果實際值 和 我的期望值相同,那么就更新
//如果實際值 和 我的期望值不同,那么就不更新
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
//因為期望值是2020 實際值卻變成了2021 所以會修改失敗
//CAS 是CPU的并發原語
atomicInteger.getAndIncrement(); //++操作
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}
我們點開AtomicInteger原始碼觀察一下,可以發現


2)總結
CAS:比較當前作業記憶體中的值 和 主記憶體中的值,如果這個值是期望的,那么則執行操作!如果不是就一直回圈,使用的是自旋鎖,
缺點:
- 一次性只能保證一個共享變數的原子性
- 回圈會耗時
- 它會存在ABA問題
什么是ABA問題?(就是我們所說的貍貓換太子)

package com.zmz.CAS;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ProjectName: Juc
* @Package: com.zmz.CAS
* @ClassName: CasDemo02
* @Author: 張晟睿
* @Date: 2021/10/12 20:40
* @Version: 1.0
*/
public class CasDemo02 {
//CAS : compareAndSet 比較并交換
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
//boolean compareAndSet(int expect, int update)
//期望值、更新值
//如果實際值 和 我的期望值相同,那么就更新
//如果實際值 和 我的期望值不同,那么就不更新
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
//因為期望值是2020 實際值卻變成了2021 所以會修改失敗
//CAS 是CPU的并發原語
// atomicInteger.getAndIncrement(); //++操作
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}
20、原子參考
為了解決ABA問題,對應的思想:就是使用了樂觀鎖~
注意:
Integer 使用了物件快取機制,默認范圍是-128~127,推薦使用靜態工廠方法valueOf獲取物件實體,而不是new,因為valueOf使用快取,而new一定會創建新的物件分配新的記憶體空間,
說明:對于Integer var = ?在-128 至127之間的賦值,Integer物件是在IntegerCache .cache產生,會復用已有物件, 這個區間內的Integer值可以直接使用==進 行判斷,但是這個區間之外的所有資料,都會在堆上產生,并不會復用已有物件,這是一個大坑,推薦使用equals方法進行判斷,
package com.zmz.CAS;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* @ProjectName: Juc
* @Package: com.zmz.CAS
* @ClassName: CasDemo03
* @Author: 張晟睿
* @Date: 2021/10/12 20:45
* @Version: 1.0
*/
public class CasDemo03 {
/**AtomicStampedReference 注意,如果泛型是一個包裝類,注意物件的參考問題
* 正常在業務操作,這里面比較的都是一個個物件
*/
static AtomicStampedReference<Integer> atomicStampedReference = new
AtomicStampedReference<>(1, 1);
// CAS compareAndSet : 比較并交換!
public static void main(String[] args) {
new Thread(() -> {
int stamp = atomicStampedReference.getStamp(); // 獲得版本號
System.out.println("a1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 修改操作時,版本號更新 + 1
atomicStampedReference.compareAndSet(1, 2,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1);
System.out.println("a2=>" + atomicStampedReference.getStamp());
// 重新把值改回去, 版本號更新 + 1
System.out.println(atomicStampedReference.compareAndSet(2, 1,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1));
System.out.println("a3=>" + atomicStampedReference.getStamp());
}, "a").start();
// 樂觀鎖的原理相同!
new Thread(() -> {
int stamp = atomicStampedReference.getStamp(); // 獲得版本號
System.out.println("b1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(1, 3,
stamp, stamp + 1));
System.out.println("b2=>" + atomicStampedReference.getStamp());
}, "b").start();
}
}

21、各種鎖的理解
1)公平鎖,非公平鎖
- 公平鎖:非常公平的鎖,不能插隊,必須先來后到

- 非公平鎖:非常不公平,允許插隊,可以改變順序

2)可重入鎖

? 1.Synchonized鎖
package com.zmz.lock;
/**
* @ProjectName: Juc
* @Package: com.zmz.lock
* @ClassName: SynchonizedDemo
* @Author: 張晟睿
* @Date: 2021/10/12 20:59
* @Version: 1.0
*/
public class SynchonizedDemo {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()+"=> 發短息");
call();//這里也有一把鎖
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName()+"=> 打電話");
}
}
? 2.Lock鎖
package com.zmz.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ProjectName: Juc
* @Package: com.zmz.lock
* @ClassName: LockDemo
* @Author: 張晟睿
* @Date: 2021/10/12 21:02
* @Version: 1.0
*/
public class LockDemo {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone2{
Lock lock=new ReentrantLock();
public void sms(){
lock.lock(); //細節:這個是兩把鎖,兩個鑰匙
//lock鎖必須配對,否則就會死鎖在里面
try {
System.out.println(Thread.currentThread().getName()+"=> 發短信");
call();//這里也有一把鎖
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void call(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "=> 打電話");
}catch (Exception e){
e.printStackTrace();
}
finally {
lock.unlock();
}
}
}
注意:
- lock鎖必須配對,相當于lock和 unlock 必須數量相同
- 在外面加的鎖,也可以在里面解鎖;在里面加的鎖,在外面也可以解鎖
3)自旋鎖
? 1.spinlock
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
? 2.自我設計自旋鎖
package com.zmz.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ProjectName: Juc
* @Package: com.zmz.lock
* @ClassName: MySpinlockTest
* @Author: 張晟睿
* @Date: 2021/10/12 21:07
* @Version: 1.0
*/
class MySpinlock {
// 默認
// int 0
//thread null
AtomicReference<Thread> atomicReference=new AtomicReference<>();
//加鎖
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"===> mylock");
//自旋鎖
while (!atomicReference.compareAndSet(null,thread)){
System.out.println(Thread.currentThread().getName()+" ==> 自旋中~");
}
}
//解鎖
public void myUnlock(){
Thread thread=Thread.currentThread();
System.out.println(thread.getName()+"===> myUnlock");
atomicReference.compareAndSet(thread,null);
}
}
public class MySpinlockTest {
public static void main(String[] args) throws InterruptedException {
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();
reentrantLock.unlock();
//使用CAS實作自旋鎖
MySpinlock spinlockDemo=new MySpinlock();
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"Thread1").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"Thread2").start();
}
}
運行結果:t2行程必須等待t1行程Unlock后,才能Unlock,在這之前進行自旋等待,
4)死鎖

package com.zmz.lock;
import java.util.concurrent.TimeUnit;
/**
* @ProjectName: Juc
* @Package: com.zmz.lock
* @ClassName: DeadLockDemo
* @Author: 張晟睿
* @Date: 2021/10/12 21:13
* @Version: 1.0
*/
public class DeadLockDemo {
public static void main(String[] args) {
String lockA= "lockA";
String lockB= "lockB";
new Thread(new MyThread1(lockA,lockB),"Thread1").start();
new Thread(new MyThread1(lockB,lockA),"Thread2").start();
}
}
class MyThread1 implements Runnable{
private String lockA;
private String lockB;
public MyThread1(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+" lock"+lockA+"===>get"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+" lock"+lockB+"===>get"+lockA);
}
}
}
}

🤡我們如何去解決死鎖的問題?
1、使用jps定位行程號,jdk的bin目錄下: 有一個jps
jps -l

2、行程行程號 找到死鎖資訊
jstack 行程號 #如果出現拒絕訪問 一定要以管理員身份運行

一般情況資訊在最后:

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/312166.html
標籤:其他
上一篇:SonarQube使用介紹
下一篇:前端組件化開發實踐總結
