Synchronized是經過監視器保證線程同步從而保證線程安全。可是Synchronized鎖能夠鎖對象和鎖類,並會產生不一樣的效果,經過下面的案例完全理解Synchronized鎖的使用方式。java
即:安全
對於普通的同步方法,鎖是當前實例對象多線程
對於靜態同步方法,鎖是該類併發
對於同步方法塊,鎖是Synchronized括號裏面配置的對象。ide
下面經過代碼具體分析幾種狀況。要想了解併發狀況,首先咱們必須知道,類信息、實例對象分別存放在什麼位置。類的信息,包括靜態變量都是存放在方法區中;而實例對象,包括類的成員變量,是存放在堆中。this
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
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,所以不會產生併發安全問題。同步
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,也不會產生線程安全問題。
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是兩個線程執行兩個實例化對象,添加的是對象鎖,至關於各自的對象鎖鎖住各自的對象,而靜態變量是類變量,存放在方法區中而不是堆中,此狀況對象鎖並不能保證線程同步,所以會產生線程安全問題。
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,兩個線程執行兩個實例化對象,因爲使用的是類鎖,也會使線程同步,保證線程安全。
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同樣,添加對象鎖,只能保證同一對象的併發同步,不能保證不一樣對象同步。
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同樣,添加類鎖,不管是多個線程操做一個實例化對象仍是多個實例化對象,都能保證線程安全。
總結:
對象鎖只能保證各自實例化對象併發的線程安全問題。類鎖能夠保證多個實例化多謝的安全問題。