synchronized是Java里的一個關鍵字,起到的一個效果是“監視器鎖”~~,它的功能就是保證操作的原子性,同時禁止指令重排序和保證內存的可見性!
public class TestDemo { static class Counter{ public int count = 0; public void add(){ count++; } } public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); Thread t1 = new Thread(){ @Override public void run() { for (int i = 0; i < 50000; i++) { counter.add(); } } }; Thread t2 = new Thread(){ @Override public void run() { for (int i = 0; i < 50000; i++) { counter.add(); } } }; //啟動兩個線程 t1.start(); t2.start(); //等待兩個線程結束 t1.join(); t2.join(); System.out.println(counter.count); } }
此時的線程就是不安全的,如何解決呢?
給我們的Counter對象里的add方法加上synchronized關鍵字,針對這個方法進行了加鎖操作。進入代碼塊(調用方法)自動加鎖,出了代碼塊(方法結束),自動解鎖。
public class TestDemo { static class Counter{ public int count = 0; //修飾方法 synchronized public void add{ count++; } } public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); Thread t1 = new Thread(){ @Override public void run() { for (int i = 0; i < 50000; i++) { counter.add(); } } }; Thread t2 = new Thread(){ @Override public void run() { for (int i = 0; i < 50000; i++) { counter.add(); } } }; //啟動兩個線程 t1.start(); t2.start(); //等待兩個線程結束 t1.join(); t2.join(); System.out.println(counter.count); } }
那么這里的代碼是如何保證正確的呢?
使用synchronized 就相當于在我們執行的指令里又加入了2條新指令。
LOCK (加鎖)
UNLOCK (解鎖)
LOCK操作特性:只有一個線程能執行成功!如果第一個線程執行成功,第二個線程也嘗試LOCK,就會阻塞等待,直到第一個線程執行UNLOCK 釋放鎖~
通過LOCK和UNLOCK 就把 LOAD ADD SAVE 這三個指令,給打包成了一個原子的操作(中間不能被打斷,也不能被其他線程穿插)。
這里的加鎖也是保證原子性的核心操作,所以線程里的沒組指令就會順序執行,不在穿插執行,就保證了線程1執行完之后再去執行線程2。
舉個例子:
就好比張三和李四去ATM里去取錢,當張三進去取錢時,進去后就會鎖門,李四就會在外面等待,直到張三取完錢出來后,李四在進去取錢。
synchronized 也會禁止編譯器進行“內存可見性”和“指令重排序”的優化~ 同時程序運行的效率就會降低,
也會導致線程之間相互去等待,就涉及到系統的一些調度,也會引入一些時間成本。
synchronized修飾的對象有以下幾種:
修飾一個代碼塊,被修飾的代碼塊稱為同步語句塊,其作用的范圍是大括號{}括起來的代碼,作用的對象是調用這個代碼塊的對象;
public class TestDemo{ public void methond() { // 進入代碼塊會鎖 this 指向對象中的鎖; // 出代碼塊會釋放 this 指向的對象中的鎖 synchronized (this) { } } public static void main(String[] args) { TestDemo demo = new TestDemo(); demo.methond(); } }
修飾一個方法,被修飾的方法稱為同步方法,其作用的范圍是整個方法,作用的對象是調用這個方法的對象;
public class TestDemo{ public synchronized void methond() { } public static void main(String[] args) { TestDemo demo = new TestDemo(); demo.methond(); // 進入方法會鎖 demo 指向對象中的鎖; // 出方法會釋放 demo 指向的對象中的鎖 } }
修改一個靜態的方法,其作用的范圍是整個靜態方法,作用的對象是這個類的所有對象;
public class TestDemo{ public synchronized static void methond() { } public static void main(String[] args) { methond(); // 進入方法會鎖 TestDemo.class 指向對象中的鎖; //出方法會釋放 TestDemo.class 指向的對象中的鎖 } }
修改一個類,其作用的范圍是synchronized后面括號括起來的部分,作用主的對象是這個類的所有對象。
public class TestDemo{ public static void methond() { // 進入代碼塊會鎖 TestDemo.class 指向對象中的鎖; //出代碼塊會釋放 TestDemo.class 指向的對象中的鎖 synchronized (TestDemo.class) { } } public static void main(String[] args) { TestDemo demo = new TestDemo(); demo.methond(); } }
總結:
- 無論synchronized關鍵字加在方法上還是對象上,如果它作用的對象是非靜態的,則它取得的鎖是對象;
- 如果synchronized作用的對象是一個靜態方法或一個類,則它取得的鎖是對類,該類所有的對象同一把鎖。
- 每個對象只有一個鎖(lock)與之相關聯,誰拿到這個鎖誰就可以運行它所控制的那段代碼。
- 實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。
拓展:
public class TestDemo { static class Counter{ public int count = 0; public void add(){ synchronized (this){ count++; } } } public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); Thread t1 = new Thread(){ @Override public void run() { for (int i = 0; i < 50000; i++) { synchronized (counter){ counter.add(); } } } }; Thread t2 = new Thread(){ @Override public void run() { for (int i = 0; i < 50000; i++) { synchronized (counter){ counter.add(); } } } }; //啟動兩個線程 t1.start(); t2.start(); //等待兩個線程結束 t1.join(); t2.join(); System.out.println(counter.count); } }
此時可以看出上述代碼,加了兩次鎖,會發生什么呢?
但是運行代碼發現程序依然正確運行?? 為什么
但是上述分析死鎖的思路是對的
只是因為synchronized內部使用特殊手段來處理了這種情況 。
這樣的操作特性我們叫做 可重入鎖
synchronized 內部記錄了當前這把鎖是哪個線程持有的~
如果當前加鎖線程和持有鎖的線程是同一個線程~
此時就并不是真的進行“加鎖操作”,而是把一個計數器加一;
如果后續該線程繼續嘗試獲取鎖,繼續判定加鎖線程和持有鎖線程是不是同一個線程,只要是同一個線程,就不真的加鎖,而是計數器+1;
如果該線程調用解鎖操作,也不是立刻就解鎖,而是計數器減1
直到計數器減成0了,才認為真的要“釋放鎖”,才允許其他線程來獲取鎖~~
總結
到此這篇關于Java中synchronized使用的文章就介紹到這了,更多相關Java中synchronized用法內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/wlm123666/article/details/119580473