今後不怕Synchronized鎖

  • Synchronized的使用

​ Synchronized是經過監視器保證線程同步從而保證線程安全。可是Synchronized鎖能夠鎖對象和鎖類,並會產生不一樣的效果,經過下面的案例完全理解Synchronized鎖的使用方式。java

即:安全

對於普通的同步方法,鎖是當前實例對象多線程

對於靜態同步方法,鎖是該類併發

對於同步方法塊,鎖是Synchronized括號裏面配置的對象。ide

下面經過代碼具體分析幾種狀況。要想了解併發狀況,首先咱們必須知道,類信息、實例對象分別存放在什麼位置。類的信息,包括靜態變量都是存放在方法區中;而實例對象,包括類的成員變量,是存放在堆中。this

1. 成員變量+普通同步方法+鎖

public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {
        add();
    }

    public synchronized void add() {
        for (int i = 0; i < 1000; i++) {
            sum++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();		//等待線程執行完
        thread2.join();		//等待線程執行完
        System.out.println(sync01.sum);
        System.out.println(new SynDemo().sum);
    }
}

result:
	2000
        0
public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {
        add();
    }

    public synchronized void add() {
        for (int i = 0; i < 1000; i++) {
            sum++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();		//等待線程執行完
        thread2.join();		//等待線程執行完
        System.out.println(sync01.sum);
        System.out.println(sync02.sum);
    }
}

result:
	1000
        1000
        0

分析:線程

​ 理解了這兩個demo再去理解同步代碼塊下的多線程安全問題,將會達到事半功倍的效果。上面兩個demo主要是想表達,成員變量和類的實例化對象同樣,都是在堆中建立,每次new對象,都會在堆中產生一個新的對象。因此第一個demo中,當在線程同步的狀況下,兩個線程去操做同一個對象,最後的結果是2000;而第二個demo中,兩個線程去操做兩個實例化對象,因此每一個對象的成員變量sum爲1000。所以咱們也能夠發現,其實在第一個demo中才會有線程安全問題,在第二個demo中是不存在線程安全問題的,有疑問能夠去掉鎖驗證一下。經過這個例子也能夠去理解爲何sping中多例是線程安全的。code

2. 成員變量+同步代碼塊+對象鎖

public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (this) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sync01.sum);
        System.out.println(new SynDemo().sum);
    }
}

result:
	2000
        0
public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (this) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sync01.sum);
        System.out.println(sync02.sum);
        System.out.println(new SynDemo().sum);
    }
}

result:
	1000
	1000
	0

分析:對象

​ 同案例1同樣,Demo1爲兩個線程執行一個實例化對象,可是加了Synchronized對象鎖,所以實現了同步,保證線程安全。Demo2爲兩個線程執行兩個實例化對象,各自利用各自的成員變量sum,所以不會產生併發安全問題。同步

3. 成員變量+同步代碼塊+類鎖

public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (SynDemo.class) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sync01.sum);
        System.out.println(new SynDemo().sum);
    }
}

result:
	2000
	0
public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {

        add();
    }

    private void add() {
        synchronized (SynDemo.class) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sync01.sum);
        System.out.println(sync02.sum);
        System.out.println(new SynDemo().sum);
    }
}

result:
	1000
	1000
	0

分析:

​ Demo1爲兩個線程執行一個實例化對象,會產生併發安全問題,可是加了同步類鎖(能夠理解爲鎖的級別比對象鎖更高),固然也能夠實現併發同步,保證線程安全。而Demo2一樣實例化兩個對象,各自操做各自的成員變量sum,也不會產生線程安全問題。

4. 靜態變量+普通方法+鎖

public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private synchronized void add() {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sum);
    }
}

result:
	2000
public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private synchronized void add() {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sum);
    }
}

輸出結果不肯定(存在線程安全問題)

分析:

​ 從案例4咱們要注意,由成員變量換成靜態變量,而上面已經講過,靜態變量存放在方法區中,全部實例化對象共享一份。再看Demo1,兩個線程執行同一個實例化對象,而後添加的是對象鎖,所以該對象鎖能鎖住該實例化對象,實現同步,保證線程安全。

​ Demo2是兩個線程執行兩個實例化對象,添加的是對象鎖,至關於各自的對象鎖鎖住各自的對象,而靜態變量是類變量,存放在方法區中而不是堆中,此狀況對象鎖並不能保證線程同步,所以會產生線程安全問題。

5. 靜態變量+靜態方法+鎖

public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    static synchronized void add() {
        for (int i = 0; i < 1000; i++) {
            sum++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sum);
    }
}

result:
	2000
public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    static synchronized void add() {
        for (int i = 0; i < 1000; i++) {
            sum++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sum);
    }
}

result:
	2000

分析:

​ 該案例相比案例4,鎖由對象鎖換成類鎖,對於Demo1,兩個線程操做一個對象,毫無疑問會使其同步。而Demo2,兩個線程執行兩個實例化對象,因爲使用的是類鎖,也會使線程同步,保證線程安全。

6. 靜態變量+同步代碼塊+對象鎖

public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (this) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sum);
    }
}

result:
	2000
public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (this) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sum);
    }
}

輸出結果不肯定(存在線程安全問題)

分析:該案例和案例4同樣,添加對象鎖,只能保證同一對象的併發同步,不能保證不一樣對象同步。

7. 靜態變量+同步代碼塊+類鎖

public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (SynDemo.class) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sum);
    }
}

result:
	2000
public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (SynDemo.class) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待線程執行完
        thread2.join();  //等待線程執行完
        System.out.println(sum);
    }
}

result:
	2000

分析:

​ 該案例同案例5同樣,添加類鎖,不管是多個線程操做一個實例化對象仍是多個實例化對象,都能保證線程安全。

總結:

對象鎖只能保證各自實例化對象併發的線程安全問題。類鎖能夠保證多個實例化多謝的安全問題

相關文章
相關標籤/搜索