1. 程式人生 > >多線程簡單實例(1)真的需要synchronized麽?

多線程簡單實例(1)真的需要synchronized麽?

寫入 clas name rac pac bsp 得到 鎖定 lock

說道多線程的安全問題,很多人想到就就是加鎖。用到synchronized關鍵字。

那就要先說說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(); } }

執行結果:

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麽?