Java學習--多執行緒案例--模擬火車票銷售(執行緒安全問題)
阿新 • • 發佈:2019-01-24
概述:火車票20張,三個視窗同時賣火車票
一.建立一個Runnable介面的實現類TicketSell,重寫run()方法
public class TicketSell implements Runnable{
private int count = 20; //票數20張
@Override
public void run() {
Thread thread = Thread.currentThread(); //使用Thread的靜態方法獲取當前Thread物件
while(true){
if(count>0 ){
try {
Thread.sleep(200); //相當於該執行緒被掛起,200ms後就緒再去搶佔CPU執行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName()+" "+(--count));
}
}
}
}
二.建立測試類,main方法中,建立三個Thread物件(三個視窗);
package com.blueSky.TicketDemo;
public class TicketTest {
public static void main(String[] args) {
TicketSell ticketSell = new TicketSell(); //3個視窗同賣20張票,使用一個Runnable介面的實現類
//第一個視窗
Thread thread1 = new Thread(ticketSell);
thread1.setName("1號視窗" );
thread1.start();
//第二個視窗
Thread thread2 = new Thread(ticketSell);
thread2.setName("2號視窗");
thread2.start();
//第三個視窗
Thread thread3 = new Thread(ticketSell);
thread3.setName("3號視窗");
thread3.start();
}
}
三.執行main方法進行測試
四 測試結果
五 問題分析
出現負數的原因:
進入條件後發生衝突,共享資料
睡眠相當於該執行緒被掛起,暫停執行,
* 假設此時count=1,執行緒1搶到CPU執行, 進入條件體,執行緒1休息200ms,該執行緒未執行count-1操作,
* 所以下一個執行緒(比如執行緒3)搶到CPU執行時,count仍為1,仍會進入條件體,然後執行語句,睡眠200ms,
* 當執行緒2到時,若兩個執行緒還在睡眠(即都未執行count-1操作),count仍為1,執行緒3執行條件體,睡眠200ms,
* 若之後執行緒1,睡眠時間到,且搶到了CPU執行,那麼count-1 此時count=0
* 然後執行緒2,3睡眠時間到,執行緒3搶到了CPU執行,那麼count-1 此時count=0-1=-1
* * 然後執行緒3搶到了CPU ,那麼執行count-1 此時count=-1-1=-2
六 .問題解決
使用同步鎖,
- 使用同步程式碼塊
package com.blueSky.TicketDemo;
public class TicketSell implements Runnable{
private int count = 20; //票數20張
@Override
public void run() {
Thread thread = Thread.currentThread(); //使用Thread的靜態方法獲取當前Thread物件
while(true){
synchronized (this) { //同步鎖,同步程式碼塊要把共享變數包進去
if(count>0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName()+" "+(--count));
}
}
}
}
}
2 封裝方法,方法上加synchronized修飾
package com.blueSky.TicketDemo;
public class TicketSell implements Runnable{
private int count = 20; //票數20張
@Override
public void run() {
Thread thread = Thread.currentThread(); //使用Thread的靜態方法獲取當前Thread物件
while(true){
// synchronized (this) {
ticketCount(thread); //封裝方法,方法上加synchronized進行修飾
// }
}
}
private synchronized void ticketCount(Thread thread) { //方法上加synchronized進行修飾,該方法為同步方法,該方法同時只能有一個執行緒訪問。
if(count>0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName()+" "+(--count));
}
}
}
七. 同步鎖好壞
優點:執行緒安全
缺點:效率低 (就像開關門要消耗時間一樣)