1. 數據不共享的情況
在探討數據共享的話題前,先來看看數據不共享的情況,每一個線程里面的數據都是獨立的,就像下面的例子,3個線程,每一個線程自己對自己的數據進行扣減,直到0為止
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--; } }
運行結果1:

運行結果2:

運行結果3:

3.線程不安全
經過上面的例子隱身出了一個問題,就是線程安全問題,在實際場景當中,這是一個非常危險的問題,例如在雙11,秒殺,活動中,很多買家同時在0點的時候按購買,但貨品只有1個,很明顯這里就是多線程處理同一個數據(貨品庫存量),那如果在線程不安全的情況下,會出現更上面例子一樣的情況,兩個人同一時間都在對同一個數字進行處理,結果有可能是,多名買家同時獲得這個商品。
為什么這樣呢,主要原因是count--這個代碼,一般情況,這行代碼做了3個動作
- 獲取count當前的值
- 對count的值進行-1的動作
- 對count重新賦值
那問題很明顯就出在第一步,假如A線程運行到這一行代碼獲取到count的值為5,接下來,B線程搶到CPU的使用前,他也執行到了這行代碼,獲取count的值也是5,因為A線程還沒有進行-1的操作
4.線程安全
那怎么辦呢,關鍵字synchronized,在run方法前加上這句的代碼就可以達到排隊執行方法的作用了,意思就是說,在執行run代碼的時候,線程先查看當前代碼塊有沒有鑰匙,如果有鑰匙,即可進入這扇門(代碼塊),然后執行里面的內容,執行完之后就會把鑰匙交出來,由下一個搶到鑰匙的人進入,並執行里面的內容。在這個搶鑰匙的過程中是人人平等,誰先搶到鑰匙,誰先進入。我們叫這塊區域“互斥區”。
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--; } }
運行結果1:

運行結果2:

