多線程簡單實例(1)真的需要synchronized麽?
說道多線程的安全問題,很多人想到就就是加鎖。用到synchronized關鍵字。
那就要先說說synchronized問什麽能夠保證線程安全了。
首先要了解線程的工作方式:線程工作分為工作內存和主內存。主內存就是堆和靜態區。當線程運行時,首先將主內存的數據拿到工作內存
然後在工作內存中運行,再將數據寫回主內存。工作內存是私有的,但是主內存卻是共享的。
那麽線程不安全的主要根源就是不能線程讀寫主內存的共享數據。
那麽判斷要不要加鎖,在什麽位置加鎖就有了依據——共享數據
下面看一個例子:
package code.thread; public class SynchronizedDome extendsThread{ int a = 0; Object obj = new Object(); @Override public void run() { synchronized(obj) { for(int i=5;i>0;i--){ System.out.println(a); a++; try { Thread.sleep(0); } catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace(); } } } } public static void main(String[] args) { SynchronizedDome dome = new SynchronizedDome(); SynchronizedDome dome2 = new SynchronizedDome(); SynchronizedDome dome3= new SynchronizedDome(); System.out.println("Thread start:"); dome.start(); dome2.start(); dome3.start(); } }
執行結果:
Thread start:
0
1
0
0
2
3
1
1
2
3
4
4
2
3
4
看到沒,並沒有發生錯亂,與預想的輸出結果一致
那麽你可能會說,這是synchronized的功勞。真的是這樣的麽,稍微改動過一下在看看
package code.thread; public class SynchronizedDome extends Thread{ int a = 0; Object obj = new Object(); @Override public void run() { //synchronized(obj) { { for(int i=5;i>0;i--){ System.out.println(a); a++; try { Thread.sleep(0); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public static void main(String[] args) { SynchronizedDome dome = new SynchronizedDome(); SynchronizedDome dome2 = new SynchronizedDome(); SynchronizedDome dome3 = new SynchronizedDome(); System.out.println("Thread start:"); dome.start(); dome2.start(); dome3.start(); } }
看到沒,還是安全的,並沒有因為沒加鎖而發生錯亂
那麽我沒就要分析一下了,根據我們上面所說的,線程的不安全是因為數據的共享
這個例子中,分別new了三個線程對象。
每個對象在棧上有一個變量a,他們分別屬於不同的對象。所以三個線程操作的都是屬於自己本身類的數據。是對象私有的。
所以不存在數據的共享,那麽就不用加鎖了。
我沒在看一個例子,讓他們數據共享,將變量定義為靜態變量
package code.thread; public class SynchronizedDome2 { public static void main(String[] args) { Dome dome = new Dome(); Thread thread = new Thread(dome); Thread thread2 = new Thread(dome); thread.start(); thread2.start(); } } class Dome implements Runnable { static int a = 0; @Override public void run() { //synchronized(this){ { for(int i=0;i<5;i++) { System.out.println(Thread.currentThread().getName()+": "+a++); } } } }
輸出結果:
Thread-1: 0
Thread-0: 1
Thread-1: 2
Thread-0: 3
Thread-1: 4
Thread-0: 5
Thread-1: 6
Thread-1: 8
Thread-0: 7
Thread-0: 9
這麽換亂,而且我們僥幸沒有得到錯誤的結果。如果多運行幾次就會看到可能會出現錯誤的結果
那麽下面用鎖來解決,看看有什麽不同。
程序就是將什麽代碼加鎖註釋去掉。
輸出結果:
Thread-0: 0
Thread-0: 1
Thread-0: 2
Thread-0: 3
Thread-0: 4
Thread-1: 5
Thread-1: 6
Thread-1: 7
Thread-1: 8
Thread-1: 9
總結:總的來說線程不安全是由於共享數據的讀寫不同步引起的。當不涉及到共享數據,也就無不安全可說了。
synchronized關鍵字保證了操作的原子性和可見性。原子性就是說,一個執行步奏完整的執行完畢,不會再執行的過程中被其他線程打斷。
可見性是說,當執行完鎖定的代碼塊後,在解鎖之前會把最新的數據寫入到主內存中。並且清空其他線程工作內存中該數據的值。保證了該數據時最新的。
多線程簡單實例(1)真的需要synchronized麽?