多執行緒併發安全問題詳解
定義:
當多個執行緒同時執行,多個執行緒之間是相互搶佔資源執行,並且搶佔是發生線上程的執行的每一步過程中,導致出現非法資料。這種現象就稱之為多執行緒的併發安全問題。
程式碼案例:
public class SellTicketDemo {
public static void main(String[] args) {
//建立票物件
Ticket t=new Ticket();
//給票的count屬性賦值為100
t.setCount(100);
SellSystem s=new SellSystem(t);
//4個執行緒對應4個售票視窗
Thread thread1=new Thread(s);
Thread thread2=new Thread(s);
Thread thread3=new Thread(s);
Thread thread4=new Thread(s);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
//賣票系統
class SellSystem implements Runnable{
//定義票物件t
private Ticket t;
public SellSystem(Ticket t) {
this.t=t;
}
@Override
public void run() {
while(true){
try {
//此處讓進來的執行緒睡一會是為了增加其他執行緒搶佔到執行權的概率
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (t.getCount()>0) {
t.setCount(t.getCount()-1);
//Thread.currentThread()表示獲取當前正在執行的執行緒,t.getCount()是t.setCount設定後的數量
System.out.println(Thread.currentThread().getName()+"賣了一張票,還剩"+t.getCount());
}
}
}
}
//票
class Ticket{
//定義票的數量屬性
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
執行結果:
異常解析:
上述出現了三種異常情況:1. 跳過數字 2. 重複數字 3. 出現負數
1. 跳過數字 :當票還剩9張時,假設小紅執行緒先搶到執行權,進入第①步,經過getCount得到9,9>0進入第②步,經過計算再用setCount將8設定為結果值,此時小蘭搶到了執行權,由於小紅已經setCount設定了結果值,小蘭進入第①步後通過getCount得到8,8>0進入第②步,經過計算以及setCount將結果值設定為7,進入第③步,列印出7執行完畢。這時小紅又搶回執行權,由於總票數是大家共享的,此時的總票數為7,小紅前兩步已完成,直接進入第③步,經過getCount得到7,然後列印出7執行完畢。因此會出現9 , 7, 7 跳過數字的情況。
2.重複數字:當票還剩4張時,假設小紅執行緒先搶到執行權,進入第①步,經過getCount得到5,5>0進入第②步,經過(t.getCount() - 1)計算變為3,正當小紅準備setCount將3設定為結果值時,小蘭搶到了執行權,由於小紅還沒有setCount設定結果值,小蘭進入第①步後通過getCount得到的還是4,4>0進入第②步,經過計算再用setCount將結果值設定為3,進入第③步,列印出3執行完畢。這時小紅又搶回執行權,由於總票數是大家共享的,此時的總票數為3,小紅接著上次未執行完的操作直接setCount,將3設定為結果值,接著進入第③步,列印出3執行完畢。因此會出現3 ,3 重複數字的情況。
3.出現負數:當票還剩1張時,假設小紅執行緒先搶到執行權,進入第①步,經過getCount得到1,1>0準備進入第②步,此時小蘭搶到了執行權,進入第①步,經過getCount仍然得到1,1>0準備進入第②步,此時又被小黑搶到了執行權,進入第①步,經過getCount仍然得到1,1>0進入第②步,經過計算以及setCount將0設為結果值,進入第③步,列印出0執行完畢。此時小紅又搶到執行權,由於小紅上次已經執行完第①步,直接進入第②步,經過getCount得到0,再減1,最後由setCount將-1設定為結果值,進入第③步,列印出-1執行完畢。此時小蘭又搶到執行權,由於小蘭上次已經執行完第①步,直接進入第②步,經過getCount得到-1,再減1,最後由setCount將-2設定為結果值,進入第③步,列印出-2執行完畢。因此會出現0, -1 , -2 負數的情況。
4. 解決方法:
通過同步程式碼塊的方式將物件鎖起來。用synchronized來將程式碼限制起來,需要鎖物件。鎖物件要求所有的執行緒都得認識。鎖物件:共享資源、方法區中的資料、this。
正確程式碼:
修改的程式碼如下:
synchronized (t) {
if (t.getCount()>0) {
t.setCount(t.getCount()-1);
//Thread.currentThread()表示獲取當前正在執行的執行緒,t.getCount()是t.setCount設定後數量
System.out.println(Thread.currentThread().getName()+"賣了一張票,還剩"+t.getCount());
}
}
最終程式碼如下:
public class SellTicketDemo {
public static void main(String[] args) {
//建立票物件
Ticket t=new Ticket();
//給票的count屬性賦值為100
t.setCount(100);
SellSystem s=new SellSystem(t);
//4個執行緒對應4個售票視窗
Thread thread1=new Thread(s);
Thread thread2=new Thread(s);
Thread thread3=new Thread(s);
Thread thread4=new Thread(s);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
//賣票系統
class SellSystem implements Runnable{
//定義票物件t
private Ticket t;
public SellSystem(Ticket t) {
this.t=t;
}
@Override
public void run() {
while(true){
try {
//此處讓進來的執行緒睡一會是為了增加其他執行緒搶佔到執行權的概率
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (t) {
if (t.getCount()>0) {
t.setCount(t.getCount()-1);
//Thread.currentThread()表示獲取當前正在執行的執行緒,t.getCount()是t.setCount設定後的數量
System.out.println(Thread.currentThread().getName()+"賣了一張票,還剩"+t.getCount());
}
}
}
}
}
//票
class Ticket{
//定義票的數量屬性
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
執行結果:
相關文章
- java多執行緒與併發 - 執行緒池詳解Java執行緒
- HashMap多執行緒併發問題分析HashMap執行緒
- 多執行緒併發同步問題及解決方案執行緒
- Java多執行緒和併發問題集Java執行緒
- 如何解決多執行緒併發問題執行緒
- 多執行緒與高併發(二)執行緒安全執行緒
- 併發與多執行緒之執行緒安全篇執行緒
- 多執行緒系列(十五) -常用併發工具類詳解執行緒
- 多執行緒系列(十六) -常用併發原子類詳解執行緒
- 多執行緒的安全問題及解決方案執行緒
- 多執行緒,你覺得你安全了?(執行緒安全問題)執行緒
- Java多執行緒中執行緒安全與鎖問題Java執行緒
- 多執行緒問題解釋執行緒
- 多執行緒併發執行及解決方法執行緒
- Java中解決多執行緒資料安全問題Java執行緒
- Java併發專題(二)執行緒安全Java執行緒
- 啃碎併發(五):Java執行緒安全特性與問題Java執行緒
- 多執行緒詳解執行緒
- 詳解多執行緒執行緒
- JAVA多執行緒併發Java執行緒
- 多執行緒併發篇——如何停止執行緒執行緒
- 5分鐘搞懂多執行緒安全問題執行緒
- 多執行緒的安全性問題(三)執行緒
- 多執行緒與高併發(一)多執行緒入門執行緒
- java併發程式設計 | 執行緒詳解Java程式設計執行緒
- iOS 多執行緒詳解iOS執行緒
- Java多執行緒詳解Java執行緒
- 多執行緒03:?執行緒傳參詳解執行緒
- 併發程式設計之多執行緒執行緒安全程式設計執行緒
- 詳解Java執行緒安全Java執行緒
- iOS多執行緒全套:執行緒生命週期,多執行緒的四種解決方案,執行緒安全問題,GCD的使用,NSOperation的使用iOS執行緒GC
- 03 執行緒安全問題執行緒
- SimpleDateFormat 執行緒安全問題ORM執行緒
- 最常見的15個Java多執行緒,併發面試問題Java執行緒面試
- Java併發實戰一:執行緒與執行緒安全Java執行緒
- Java 併發程式設計 | 執行緒池詳解Java程式設計執行緒
- java多執行緒與併發 - 併發工具類Java執行緒
- Python執行緒安全問題及解決方法Python執行緒