JUC系列(五)| Synchonized关键字进一步理解
多線程一直Java開發中的難點,也是面試中的常客,趁著還有時間,打算鞏固一下JUC方面知識,我想機會隨處可見,但始終都是留給有準備的人的,希望我們都能加油!!!
沉下去,再浮上來,我想我們會變的不一樣的。
synchronized 實現同步的基礎:Java 中的每一個對象都可以作為鎖。
一次偶然在家陽臺上拍下來的,喜歡這樣的天
一、對于普通同步方法:
對于普通同步方法,鎖的是當前實例對象。
一個對象里面如果有多個 synchronized非靜態方法,某一個時刻內,只要一個線程去調用了其中的 一個用synchronized修飾的方法, 其它的線程都只能等待。換句話說,某一個時刻內,只能有唯一一個線程去訪問這些 synchronized 方法。
(注:當只有一個對象實例時)。
public class Synchronized8Demo {public static void main(String[] args) throws InterruptedException {Demo demo = new Demo();new Thread(()->{demo.test1();},"AA").start();new Thread(()->{demo.test2();},"BB").start();} } class Demo{public synchronized void test1(){try {Thread.sleep(1000);for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+":: 循環第"+i+"次");}}catch (Exception e){e.printStackTrace();}}public synchronized void test2(){System.out.println(Thread.currentThread().getName()+":: 只循環一次的方法");} }運行結果:
通過運行結果我們看到,當A線程進入的由synchronized修飾的test1()的方法后,B線程只有等待A線程釋放鎖,才能進入由synchronized修飾的test2(),以此可以說明當只有一個實例對象時,一個對象里面如果有多個 synchronized非靜態方法,某一個時刻內,只要一個線程去調用了其中的 一個用synchronized修飾的方法, 其它的線程都只能等待。
原因:因為鎖的是當前對象 this,被鎖定后,其它的線程都不能進入到當前對象的其它的 synchronized 方法
上面test1()的方法代碼也可以換成這樣,結果也是一樣的。
public void test1() {synchronized (this) {try {Thread.sleep(1000);for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + ":: 循環第" + i + "次");}} catch (Exception e) {e.printStackTrace();}} }補充:
1、但是加個普通方法,是和同步鎖無關的。
public class Synchronized8Demo {public static void main(String[] args) throws InterruptedException {Demo demo = new Demo();new Thread(() -> {demo.test1();}, "AA").start();new Thread(() -> {demo.test2();}, "BB").start();new Thread(() -> {demo.test3();}, "CC").start();} }class Demo {public void test1() {synchronized (this) {try {Thread.sleep(1000);for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + ":: 循環第" + i + "次");}} catch (Exception e) {e.printStackTrace();}}}public synchronized void test2() {System.out.println(Thread.currentThread().getName() + ":: 只循環一次的方法");}public void test3(){for (int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+"::沒有synchronized關鍵字的普通方法");}} }運行結果:
結論:另外加一個普通方法,執行起來是完全和同步鎖無關的,也無需等待。
2、另外如果換成兩個對象,情況也會不一樣
public class Synchronized8Demo {public static void main(String[] args) throws InterruptedException {Demo demo = new Demo();new Thread(() -> {demo.test1();}, "AA").start();Demo demo1 = new Demo();new Thread(() -> {demo1.test2();}, "BB").start();} }class Demo {public void test1() {synchronized (this) {try {Thread.sleep(1000);for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + ":: 循環第" + i + "次");}} catch (Exception e) {e.printStackTrace();}}}public synchronized void test2() {System.out.println(Thread.currentThread().getName() + ":: 只循環一次的方法");} }運行結果:
無需等待A線程釋放鎖,即可執行。
小結:
即如果一個實例對象的非靜態同步方法得到鎖之后,此實例對象的其他非靜態同步方法必須一直等到獲取到鎖的方法釋放鎖之后,才能重新獲取到鎖。但是另外的實例對象的非靜態同步方法和此實例對象用的并非一把鎖,所以無需等待此實例對象已經獲取到的鎖的非靜態同步方法釋放鎖就可以去獲取屬于他們的鎖。
下面看看靜態同步方法是什么樣的吧👇
二、對于靜態同步方法:
對于靜態同步方法,鎖是當前類的 Class 對象。
例如:我們修改一下上面的那個例子。把test2()方法改成靜態方法,看看結果是什么樣的吧。
public static synchronized void test2() {System.out.println(Thread.currentThread().getName() + ":: 只循環一次的方法"); }運行結果:
所有的靜態同步方法用的也是同一把鎖——類對象本身,而非靜態同步方法鎖的當前實例對象,所以這兩把鎖是兩個不同的對象,所以靜態同步方法與非靜態同步方法之間是不會產生競態條件的。
和之前一樣,如果一個靜態同步方法獲取到鎖之后,其他的靜態同步方法都必須等待此方法釋放鎖之后,才能繼續獲取到鎖。并且不管是一個實例對象的靜態同步方法之間,還是不同的實例對象靜態同步放之間,他們都必須遵守,因為他們是同一個類的實例對象。
此處代碼書寫方式勿怪,為測試刻意而為。
public class Synchronized8Demo {public static void main(String[] args) throws InterruptedException {Demo demo = new Demo();new Thread(() -> {demo.test1();}, "AA").start();Demo demo1 = new Demo();new Thread(() -> {demo1.test2();}, "BB").start();} }class Demo {public static synchronized void test1() {try {Thread.sleep(1000);for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + ":: 循環第" + i + "次");}} catch (Exception e) {e.printStackTrace();}}public static synchronized void test2() {System.out.println(Thread.currentThread().getName() + ":: 只循環一次的方法");} }三、對于同步方法塊:
對于同步方法塊,鎖是 Synchonized 括號里配置的對象
當一個線程試圖訪問同步代碼塊時,它首先必須得到鎖,退出或拋出異常時必須釋放鎖。
測試一: 當鎖的對象相同時,產生競爭條件,形成競爭狀態。
public class Synchronized8Demo {public static void main(String[] args) throws InterruptedException {Demo demo = new Demo();new Thread(()->{demo.test4();},"CC").start();new Thread(()->{demo.test5();},"DD").start();} } class Demo {public void test4() {synchronized (Demo.class){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+"::測試同步代碼塊");}}}public void test5(){synchronized (Demo.class){System.out.println(Thread.currentThread().getName()+"::測試同步代碼塊");}} }測試二:當鎖的對象不同時,無須等待test4()方法釋放鎖,即可執行test5()方法,
原因:因為不是同一把鎖,不產生競爭條件。
public class Synchronized8Demo {public static void main(String[] args) throws InterruptedException {Demo demo = new Demo();new Thread(()->{demo.test4();},"CC").start();new Thread(()->{demo.test5();},"DD").start();} } class Demo {public void test4() {synchronized (this){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+"::測試同步代碼塊");}}}public void test5(){synchronized (Demo.class){System.out.println(Thread.currentThread().getName()+"::測試同步代碼塊");}} }小結:
對于同步方法塊,關鍵就是 synchronized () 括號中鎖的對象是誰。
四、自言自語
最近又開始了JUC的學習,感覺Java內容真的很多,但是為了能夠走的更遠,還是覺得應該需要打牢一下基礎。
最近在持續更新中,如果你覺得對你有所幫助,也感興趣的話,關注我吧,讓我們一起學習,一起討論吧。
你好,我是博主寧在春,Java學習路上的一顆小小的種子,也希望有一天能扎根長成蒼天大樹。
希望與君共勉😁
待我們,別時相見時,都已有所成。
總結
以上是生活随笔為你收集整理的JUC系列(五)| Synchonized关键字进一步理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JUC系列(二)回顾Synchroniz
- 下一篇: 史上最详细Docker安装Elastic