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