1. 程式人生 > >Java學習--多執行緒案例--模擬火車票銷售(執行緒安全問題)

Java學習--多執行緒案例--模擬火車票銷售(執行緒安全問題)

概述:火車票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
六 .問題解決

使用同步鎖,

  1. 使用同步程式碼塊
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));
        }
    }

}

七. 同步鎖好壞
優點:執行緒安全
缺點:效率低 (就像開關門要消耗時間一樣)