1. 程式人生 > 其它 >6.18Java多執行緒併發、同步效能分析

6.18Java多執行緒併發、同步效能分析

6.18Java多執行緒併發、同步效能分析

對比同步塊和同步方法--->粒度更小的鎖定資源,儘可能地提升效能

根據幾個同步鎖物件不同的例項觀察執行緒不安全的例項

package iostudy.synchro;

/**
* 測試同步方法和同步塊對粒度更小地資源鎖定
* 感受效能上地差異
* @since JDK 1.8
* @date 2021/06/18
* @author Lucifer
*/
public class SynBlockTestNo3 {
public static void main(String[] args) {

/*資源實現類*/
SynWeb12306 synWeb12306 = new SynWeb12306();

/*多個執行緒代理物件*/
new Thread(synWeb12306, "代勞").start();
new Thread(synWeb12306, "一樓").start();
new Thread(synWeb12306, "丙樓").start();

}
}

/**
* 建立內部資源類
*/
class SynWeb12306 implements Runnable{

/*設定資源數量*/
private int ticketNums = 10;
private boolean flag = true;

/*重寫介面run方法,實現具體邏輯*/
@Override
public void run(){
while (flag){
/*模擬執行緒等待*/
try {
Thread.sleep(100);
}catch (InterruptedException e){
System.out.println(e.getMessage());
e.printStackTrace();
}

/*呼叫內部具體實現邏輯的方法*/
test3();

}
}

/**
* 寫一個內部具體實現邏輯的方法
* 同步方法
*/
public synchronized void test1(){
if (ticketNums<0){
/*開關改變*/
flag = false;
/*結束方法*/
return;
}

/*模擬延時*/
try {
Thread.sleep(200);
}catch (InterruptedException e){
System.out.println(e.getMessage());
e.printStackTrace();
}

/*獲取執行緒名稱、資源數量減少*/
System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

}

/**
* 寫另一個一樣邏輯的實現方法
* 同步塊
*/
public void test2(){

/*明確鎖的物件為資源物件*/
synchronized (this){
if (ticketNums<0){
/*開關改變*/
flag = false;
/*結束方法*/
return;
}

/*模擬延時等待*/
try {
Thread.sleep(200);
}catch (InterruptedException e){
System.out.println(e.getMessage());
e.printStackTrace();
}

/*獲取當前執行緒名稱和資源數量*/
System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

}
}

/*只鎖定資源*/

/**
* 這樣鎖會不安全,導致資料不準確
* 原因是因為ticketNums這個資源是會變的,在鎖資源的時候大的物件不能變動,裡面的資源隨便變化
* 大的物件不變,小的資源物件不變。
* 搞清楚物件在變和物件的屬性在變的區別
*/
public void test3(){

/*明確鎖的物件為資源物件*/
synchronized ((Integer) ticketNums){
if (ticketNums<0){
/*開關改變*/
flag = false;
/*結束方法*/
return;
}

/*模擬延時等待*/
try {
Thread.sleep(200);
}catch (InterruptedException e){
System.out.println(e.getMessage());
e.printStackTrace();
}

/*獲取當前執行緒名稱和資源數量*/
System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

}
}
}

粒度更小的鎖提升效能

/*只鎖定物件的屬性--->縮小區域--->還是會有執行緒不安全問題。範圍太小鎖不住*/
/*小粒度鎖定資源觀察資料是否安全*/
public void test4(){

/*只鎖定this的ticketNums屬性*/
synchronized (this){
if (ticketNums<0){
/*開關變化*/
flag = false;
/*結束方法*/
return;
}
}

/*模擬網路延遲*/
try {
Thread.sleep(200);
}catch (InterruptedException e){
System.out.println(e.getMessage());
e.printStackTrace();
}

/*獲取當前執行緒名稱以及資源數量*/
System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

}
/*鎖定範圍太大--->損耗效能資源*/
/**
* 寫另一個一樣邏輯的實現方法
* 同步塊
*/
public void test2(){

/*明確鎖的物件為資源物件*/
synchronized (this){
if (ticketNums<0){
/*開關改變*/
flag = false;
/*結束方法*/
return;
}

/*模擬延時等待*/
try {
Thread.sleep(200);
}catch (InterruptedException e){
System.out.println(e.getMessage());
e.printStackTrace();
}

/*獲取當前執行緒名稱和資源數量*/
System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

}
}

double checking保證資料安全

  /**
* 寫另一個一樣邏輯的實現方法
* 同步塊,縮小鎖定範圍
*/
public void test5(){

/*明確鎖的物件為資源物件*/
if (ticketNums<0){
/*開關改變*/
flag = false;
/*結束方法*/
return;
}
/*
上面的部分考慮的是沒有票的情況
如果有多個執行緒進來最開始攔截不住還是會出現資料不安全的情況
*/

/*只鎖定下面部分的範圍*/
synchronized (this){
if (ticketNums<0){
/*開關改變*/
flag = false;
/*結束方法*/
return;
}
/*
這裡加入判斷考慮最後一張票的情況
*/

/*模擬延時等待*/
try {
Thread.sleep(200);
}catch (InterruptedException e){
System.out.println(e.getMessage());
e.printStackTrace();
}

/*獲取當前執行緒名稱和資源數量*/
System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

}
/*
儘可能地考慮鎖合理地範圍不是指程式碼地數量而是指資料地完整性
--->雙重檢測(double checking)
執行緒安全
兩個檢查,第一個檢查不用鎖定,第二個檢查需要鎖定
*/
}