Java多線程學習之線程數據安全(二)

1. 數據不共享的情況

在探討數據共享的話題前,先來看看數據不共享的情況,每一個線程裏面的數據都是獨立的,就像下面的例子,3個線程,每一個線程自己對自己的數據進行扣減,直到0爲止

代碼demo:

public class TestThread {
    public static void main(String[] args) {

        MyThread myThread_A = new MyThread("A");
        MyThread myThread_B = new MyThread("B");
        MyThread myThread_C = new MyThread("C");

        myThread_A.start();
        myThread_B.start();
        myThread_C.start();

    }

}

class MyThread extends Thread {

    private int count = 5;
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        // super.run();

        while (count != 0) {
            System.out.println(name + " = " + count);
            count--;
        }
    }
}

2. 數據共享的情況

這裏先看一段測試代碼,看看共享數據的結構,按照理想的情況下,MyRunnable只實例了一次,所以裏面的count=5將會被扣減5次,打印的結果應該是,5,4,3,2,1,因爲這裏有5個線程,每次調用run的時候,都會減去1,但是結果,確實有點出乎意外....而且每一次執行的結果都不一樣

public class TestThread {
    public static void main(String[] args) {

        //新建一個帶有Runnable接口的類
        MyRunnable myRunnable = new MyRunnable();

        //新建5個線程,但使用了同一個Runnable實例對象,意味着裏面的數據是共享的
        //這裏的Thread(Runnable,String)是一個構造函數,第一次參數爲Runnable接口,第二個爲線程名稱
        Thread thread_A = new Thread(myRunnable,"A");
        Thread thread_B = new Thread(myRunnable,"B");
        Thread thread_C = new Thread(myRunnable,"C");
        Thread thread_D = new Thread(myRunnable,"D");
        Thread thread_E = new Thread(myRunnable,"E");

        thread_A.start();
        thread_B.start();
        thread_C.start();
        thread_D.start();
        thread_E.start();

    }

}

class MyRunnable implements Runnable {

    private int count = 5;

    public MyRunnable() {
    }

    @Override
    public void run() {
        // super.run();
        System.out.println("當前線程名稱" + Thread.currentThread().getName() + " = " + count);
        count--;

    }
}

 第一次結果:

第二次結果:

 第三次結果:

3.線程不安全

經過上面的例子隱身出了一個問題,就是線程安全問題,在實際場景當中,這是一個非常危險的問題,例如在雙11,秒殺,活動中,很多買家同時在0點的時候按購買,但貨品只有1個,很明顯這裏就是多線程處理同一個數據(貨品庫存量),那如果在線程不安全的情況下,會出現更上面例子一樣的情況,兩個人同一時間都在對同一個數字進行處理,結果有可能是,多名買家同時獲得這個商品。

爲什麼這樣呢,主要原因是count--這個代碼,一般情況,這行代碼做了3個動作

  1. 獲取count當前的值
  2. 對count的值進行-1的動作
  3. 對count重新賦值

那問題很明顯就出在第一步,假如A線程運行到這一行代碼獲取到count的值爲5,接下來,B線程搶到CPU的使用前,他也執行到了這行代碼,獲取count的值也是5,因爲A線程還沒有進行-1的操作

4.線程安全

那怎麼辦呢,關鍵字synchronized,在run方法前加上這句的代碼就可以達到排隊執行方法的作用了,意思就是說,在執行run代碼的時候,線程先查看當前代碼塊有沒有鑰匙,如果有鑰匙,即可進入這扇門(代碼塊),然後執行裏面的內容,執行完之後就會把鑰匙交出來,由下一個搶到鑰匙的人進入,並執行裏面的內容。在這個搶鑰匙的過程中是人人平等,誰先搶到鑰匙,誰先進入。我們叫這塊區域“互斥區”。

 

package com.xu.chap2;

public class TestThread2 {

    public static void main(String[] args) {

        //新建一個帶有Runnable接口的類
        MyRunnable myRunnable = new MyRunnable();

        //新建5個線程,但使用了同一個Runnable實例對象,意味着裏面的數據是共享的
        //這裏的Thread(Runnable,String)是一個構造函數,第一次參數爲Runnable接口,第二個爲線程名稱
        Thread thread_A = new Thread(myRunnable, "A");
        Thread thread_B = new Thread(myRunnable, "B");
        Thread thread_C = new Thread(myRunnable, "C");
        Thread thread_D = new Thread(myRunnable, "D");
        Thread thread_E = new Thread(myRunnable, "E");

        thread_A.start();
        thread_B.start();
        thread_C.start();
        thread_D.start();
        thread_E.start();

    }

}

class MyRunnable implements Runnable {

    private int count = 5;

    public MyRunnable() {
    }

    @Override
    synchronized public void run() {
        // super.run();
        System.out.println("當前線程名稱" + Thread.currentThread().getName() + " = " + count);
        count--;

    }
}

第一次運行結果:

第二次運行結果:

至此,我們就成功實現了線程數據安全了,我們下回再見

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章