Java基礎之線程安全

回顧

在上一篇 Java基礎之多線程編程,咱們講解了多線程的實現,運行起來彷佛也沒什麼問題,可是咱們若加一段代碼java

class Window implements Runnable{//實現接口
    int ticket=100;
    @Override
    public void run() {
        while (true){
            if(ticket>0){
                try {
                    Thread.currentThread().sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"售票:票號爲:"+ ticket--);
            }else{
                break;
            }
        }
    }
}
Window w=new Window();
        Thread t1=new Thread(w);
        Thread t2=new Thread(w);
        Thread t3=new Thread(w);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t1.start();
        t2.start();
複製代碼

相比與上一篇咱們的代碼裏多了編程

Thread.currentThread().sleep(10);
複製代碼

sleep()方法使得當前線程阻塞10毫秒,咱們看代碼運行效果安全

票賣超了,怎麼回事?按代碼邏輯來看,好像並沒什麼問題?

這就是存在 線程不安全bash

問題分析

那麼咱們分析下如何出現的這個現象? 咱們假設有兩個賣票線程:線程A和線程B ,此時餘票還有1張,看下圖多線程

文字解釋下,當ticket=1時,線程A進入if判斷內,接着線程A進入sleep狀態,緊接着線程B得到cpu執行權開始執行, 此時ticket=1,進入if判斷內 也開始sleep,而後線程A的sleep結束 恢復,開始執行,並把ticket--,此時ticket=0,而後線程B恢復, 打印tickect爲0,ticket再次-1.變成了-1. 這就時三個窗口同時賣票,票賣超的緣由,也稱線程不安全。ide

線程安全

上面咱們分析了致使線程不安全出現的緣由?那怎麼解決呢?post

  • 咱們但願一個線程操做共享數據結束之後,其餘的線程纔有機會參與共享數據的操做。

線程安全是多線程編程時的計算機程序代碼中的一個概念。在擁有共享數據的多條線程並行執行的程序中,線程安全的代碼會經過同步機制保證各個線程均可以正常且正確的執行,不會出現數據污染等意外狀況。ui

用java代碼來實現,主要有兩種方法:this

線程的同步機制

方法一 :同步代碼塊

格式以下 使用synchronized關鍵字spa

synchronized(同步監視器){
    //須要被同步的代碼塊(操做共享數據的代碼塊)
}
複製代碼

以上同步監視器 又稱爲「鎖」,鎖須要惟一,代碼以下

class Window implements Runnable{//實現接口
    int ticket=100;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if(ticket>0){
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"售票:票號爲:"+ ticket--);
                }
            }
        }
    }
}
複製代碼

執行後以下,正常!

方法二 :同步方法

將操做共享數據的代碼 提取到一個方法內 而後用synchronized 修飾

synchronized void show(){
    //操做共享數據的代碼
}
複製代碼

修改賣票程序用同步方法實現以下:

class Window implements Runnable{//實現接口
    int ticket=100;
    public synchronized void show(){
        if(ticket>0){
            try {
                Thread.currentThread().sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"售票:票號爲:"+ ticket--);
        }
    }
    @Override
    public  void run() {
        while (true){
            this.show();
        }
    }
}

複製代碼

注意在同步方法實現中 鎖默認爲this 也須要惟一, 咱們用圖解釋下線程安全下兩個線程如何操做共享數據的:

由上圖咱們知道,一旦遇到操做共享數據時,線程老是同步執行的。

總結

  • 遇到多個線程操做共享數據時就會出現線程不安全問題
  • 同步方法或者同步代碼塊 都是爲了讓線程同步執行
  • 同步方法和同步代碼塊都須要一個對象 做爲鎖,這個鎖要確保惟一性

喜歡本文的朋友們,歡迎長按下圖關注訂閱號"個人編程筆記",收看更多精彩內容~~

相關文章
相關標籤/搜索