<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java之戳中痛點(diǎn)之 synchronized 深度解析

          共 3739字,需瀏覽 8分鐘

           ·

          2020-08-21 23:58

          點(diǎn)擊上方藍(lán)色“程序猿DD”,選擇“設(shè)為星標(biāo)”

          回復(fù)“資源”獲取獨(dú)家整理的學(xué)習(xí)資料!

          來源 |?https://blog.csdn.net/jcmj123456/article/details/107886103

          概覽:

          • 簡(jiǎn)介:作用、地位、不控制并發(fā)的影響

          • 用法:對(duì)象鎖和類鎖

          • 多線程訪問同步方法的7種情況

          • 性質(zhì):可重入、不可中斷

          • 原理:加解鎖原理、可重入原理、可見性原理

          • 缺陷:效率低、不夠靈活、無法預(yù)判是否成功獲取到鎖

          • 如何選擇Lock或Synchronized

          • 如何提高性能、JVM如何決定哪個(gè)線程獲取鎖

          • 總結(jié)

          后續(xù)會(huì)有代碼演示,測(cè)試環(huán)境 JDK8、IDEA

          一、簡(jiǎn)介

          1、作用

          能夠保證在同一時(shí)刻最多只有一個(gè)線程執(zhí)行該代碼,以保證并發(fā)安全的效果。

          2、地位

          • Synchronized是Java關(guān)鍵字,Java原生支持

          • 最基本的互斥同步手段

          • 并發(fā)編程的元老級(jí)別

          3、不控制并發(fā)的影響

          測(cè)試:兩個(gè)線程同時(shí)a++,猜一下結(jié)果

          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?不使用synchronized,兩個(gè)線程同時(shí)a++
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedTest1?implements?Runnable{
          ????static?SynchronizedTest1?st?=?new?SynchronizedTest1();

          ????static?int?a?=?0;

          ????/**
          ?????*?不使用synchronized,兩個(gè)線程同時(shí)a++
          ?????*/

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(st);
          ????????Thread?t2?=?new?Thread(st);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println(a);
          ????}

          ????@Override
          ????public?void?run(){
          ????????for(int?i=0;?i<10000;?i++){
          ????????????a++;
          ????????}
          ????}
          }

          預(yù)期是20000,但多次執(zhí)行的結(jié)果都小于20000

          10108
          11526
          10736
          ...

          二、用法:對(duì)象鎖和類鎖

          1、對(duì)象鎖

          • 代碼塊形式:手動(dòng)指定鎖對(duì)象

          • 方法鎖形式:synchronized修飾方法,鎖對(duì)象默認(rèn)為this


          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?對(duì)象鎖實(shí)例:?代碼塊形式
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedTest2?implements?Runnable{
          ????static?SynchronizedTest2?st?=?new?SynchronizedTest2();

          ????public?static?void?main(String[]?args)?{
          ????????Thread?t1?=?new?Thread(st);
          ????????Thread?t2?=?new?Thread(st);
          ????????t1.start();
          ????????t2.start();
          ????????while(t1.isAlive()?||?t2.isAlive()){

          ????????}
          ????????System.out.println("run?over");

          ????}

          ????@Override
          ????public?void?run(){
          ????????synchronized?(this){
          ????????????System.out.println("開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????????try?{
          ????????????????//?模擬執(zhí)行內(nèi)容
          ????????????????Thread.sleep(3000);
          ????????????}catch?(Exception?e){
          ????????????????e.printStackTrace();
          ????????????}
          ????????????System.out.println("執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????????}
          ????}
          }


          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?對(duì)象鎖實(shí)例:synchronized方法
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedTest3?implements?Runnable{
          ????static?SynchronizedTest3?st?=?new?SynchronizedTest3();

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(st);
          ????????Thread?t2?=?new?Thread(st);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println("run?over");
          ????}

          ????@Override
          ????public?void?run(){
          ????????method();
          ????}

          ????public?synchronized?void?method(){
          ????????System.out.println("開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}
          }

          結(jié)果:

          開始執(zhí)行:Thread-0
          執(zhí)行結(jié)束:Thread-0
          開始執(zhí)行:Thread-1
          執(zhí)行結(jié)束:Thread-1
          run?over

          2、類鎖

          概念:Java類可能有多個(gè)對(duì)象,但只有一個(gè)Class對(duì)象

          本質(zhì):所謂的類鎖,不過是Class對(duì)象的鎖而已

          用法和效果:類鎖只能在同一時(shí)刻被一個(gè)對(duì)象擁有

          形式1:synchronized加載static方法上

          形式2:synchronized(*.class)代碼塊

          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?類鎖:synchronized加載static方法上
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedTest4?implements?Runnable{

          ????static?SynchronizedTest4?st1?=?new?SynchronizedTest4();
          ????static?SynchronizedTest4?st2?=?new?SynchronizedTest4();

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(st1);
          ????????Thread?t2?=?new?Thread(st2);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println("run?over");
          ????}

          ????@Override
          ????public?void?run(){
          ????????method();
          ????}

          ????public?static?synchronized?void?method(){
          ????????System.out.println("開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}
          }


          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?類鎖:synchronized(*.class)代碼塊
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedTest5?implements?Runnable{
          ????static?SynchronizedTest4?st1?=?new?SynchronizedTest4();
          ????static?SynchronizedTest4?st2?=?new?SynchronizedTest4();

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(st1);
          ????????Thread?t2?=?new?Thread(st2);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println("run?over");
          ????}

          ????@Override
          ????public?void?run(){
          ????????method();
          ????}

          ????public?void?method(){
          ????????synchronized(SynchronizedTest5.class){
          ????????????System.out.println("開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????????try?{
          ????????????????//?模擬執(zhí)行內(nèi)容
          ????????????????Thread.sleep(3000);
          ????????????}catch?(Exception?e){
          ????????????????e.printStackTrace();
          ????????????}
          ????????????System.out.println("執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????????}
          ????}
          }

          結(jié)果:

          開始執(zhí)行:Thread-0
          執(zhí)行結(jié)束:Thread-0
          開始執(zhí)行:Thread-1
          執(zhí)行結(jié)束:Thread-1
          run?over


          三、多線程訪問同步方法的7種情況

          1. 兩個(gè)線程同時(shí)訪問一個(gè)對(duì)象的相同的synchronized方法

          2. 兩個(gè)線程同時(shí)訪問兩個(gè)對(duì)象的相同的synchronized方法

          3. 兩個(gè)線程同時(shí)訪問兩個(gè)對(duì)象的相同的static的synchronized方法

          4. 兩個(gè)線程同時(shí)訪問同一對(duì)象的synchronized方法與非synchronized方法

          5. 兩個(gè)線程訪問同一對(duì)象的不同的synchronized方法

          6. 兩個(gè)線程同時(shí)訪問同一對(duì)象的static的synchronized方法與非static的synchronized方法

          7. 方法拋出異常后,會(huì)釋放鎖嗎

          仔細(xì)看下面示例代碼結(jié)果輸出的結(jié)果,注意輸出時(shí)間間隔,來預(yù)測(cè)結(jié)論

          場(chǎng)景1:

          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?兩個(gè)線程同時(shí)訪問一個(gè)對(duì)象的相同的synchronized方法
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedScene1?implements?Runnable{
          ????static?SynchronizedScene1?ss?=?new?SynchronizedScene1();

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(ss);
          ????????Thread?t2?=?new?Thread(ss);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println("run?over");
          ????}

          ????@Override
          ????public?void?run(){
          ????????method();
          ????}

          ????public?synchronized?void?method(){
          ????????System.out.println("開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}
          }

          場(chǎng)景2:

          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?兩個(gè)線程同時(shí)訪問兩個(gè)對(duì)象的相同的synchronized方法
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedScene2?implements?Runnable{
          ????static?SynchronizedScene2?ss1?=?new?SynchronizedScene2();
          ????static?SynchronizedScene2?ss2?=?new?SynchronizedScene2();

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(ss1);
          ????????Thread?t2?=?new?Thread(ss2);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println("run?over");
          ????}

          ????@Override
          ????public?void?run(){
          ????????method();
          ????}

          ????public?synchronized?void?method(){
          ????????System.out.println("開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}
          }

          場(chǎng)景3:

          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?兩個(gè)線程同時(shí)訪問兩個(gè)對(duì)象的相同的static的synchronized方法
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedScene3?implements?Runnable{
          ????static?SynchronizedScene3?ss1?=?new?SynchronizedScene3();
          ????static?SynchronizedScene3?ss2?=?new?SynchronizedScene3();

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(ss1);
          ????????Thread?t2?=?new?Thread(ss2);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println("run?over");
          ????}

          ????@Override
          ????public?void?run(){
          ????????method();
          ????}

          ????public?synchronized?static?void?method(){
          ????????System.out.println("開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}
          }

          場(chǎng)景4:

          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?兩個(gè)線程同時(shí)訪問同一對(duì)象的synchronized方法與非synchronized方法
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedScene4?implements?Runnable{
          ????static?SynchronizedScene4?ss1?=?new?SynchronizedScene4();

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(ss1);
          ????????Thread?t2?=?new?Thread(ss1);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println("run?over");
          ????}

          ????@Override
          ????public?void?run(){
          ????????//?模擬兩個(gè)線程同時(shí)訪問?synchronized方法與非synchronized方法
          ????????if(Thread.currentThread().getName().equals("Thread-0")){
          ????????????method1();
          ????????}else{
          ????????????method2();
          ????????}
          ????}

          ????public?void?method1(){
          ????????System.out.println("method1開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("method1執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}

          ????public?synchronized?void?method2(){
          ????????System.out.println("method2開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("method2執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}
          }

          場(chǎng)景5:

          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?兩個(gè)線程訪問同一對(duì)象的不同的synchronized方法
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedScene5?implements?Runnable{
          ????static?SynchronizedScene5?ss1?=?new?SynchronizedScene5();

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(ss1);
          ????????Thread?t2?=?new?Thread(ss1);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println("run?over");
          ????}

          ????@Override
          ????public?void?run(){
          ????????//?模擬兩個(gè)線程同時(shí)訪問不同的synchronized方法
          ????????if(Thread.currentThread().getName().equals("Thread-0")){
          ????????????method1();
          ????????}else{
          ????????????method2();
          ????????}
          ????}

          ????public?synchronized?void?method1(){
          ????????System.out.println("method1開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("method1執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}

          ????public?synchronized?void?method2(){
          ????????System.out.println("method2開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("method2執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}
          }

          場(chǎng)景6:

          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?兩個(gè)線程同時(shí)訪問同一對(duì)象的static的synchronized方法與非static的synchronized方法
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedScene6?implements?Runnable{
          ????static?SynchronizedScene6?ss1?=?new?SynchronizedScene6();

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(ss1);
          ????????Thread?t2?=?new?Thread(ss1);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println("run?over");
          ????}

          ????@Override
          ????public?void?run(){
          ????????//?模擬兩個(gè)線程同時(shí)訪問static的synchronized方法與非static的synchronized方法
          ????????if(Thread.currentThread().getName().equals("Thread-0")){
          ????????????method1();
          ????????}else{
          ????????????method2();
          ????????}
          ????}

          ????public?static?synchronized?void?method1(){
          ????????System.out.println("method1開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("method1執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}

          ????public?synchronized?void?method2(){
          ????????System.out.println("method2開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????System.out.println("method2執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}
          }

          場(chǎng)景7:

          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?方法拋出異常后,會(huì)釋放鎖嗎
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedScene7?implements?Runnable{
          ????static?SynchronizedScene7?ss1?=?new?SynchronizedScene7();

          ????public?static?void?main(String[]?args)?throws?Exception{
          ????????Thread?t1?=?new?Thread(ss1);
          ????????Thread?t2?=?new?Thread(ss1);
          ????????t1.start();
          ????????t2.start();
          ????????t1.join();
          ????????t2.join();
          ????????System.out.println("run?over");
          ????}

          ????@Override
          ????public?void?run(){
          ????????method1();
          ????}

          ????public?synchronized?void?method1(){
          ????????System.out.println("method1開始執(zhí)行:"?+?Thread.currentThread().getName());
          ????????try?{
          ????????????//?模擬執(zhí)行內(nèi)容
          ????????????Thread.sleep(3000);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????????//?模擬異常
          ????????throw?new?RuntimeException();
          ????????//System.out.println("method1執(zhí)行結(jié)束:"?+?Thread.currentThread().getName());
          ????}
          }


          總結(jié):

          1、兩個(gè)線程同時(shí)訪問一個(gè)對(duì)象的相同的synchronized方法

          同一實(shí)例擁有同一把鎖,其他線程必然等待,順序執(zhí)行

          2、兩個(gè)線程同時(shí)訪問兩個(gè)對(duì)象的相同的synchronized方法

          不同的實(shí)例擁有的鎖是不同的,所以不影響,并行執(zhí)行

          3、兩個(gè)線程同時(shí)訪問兩個(gè)對(duì)象的相同的static的synchronized方法

          靜態(tài)同步方法,是類鎖,所有實(shí)例是同一把鎖,其他線程必然等待,順序執(zhí)行

          4、兩個(gè)線程同時(shí)訪問同一對(duì)象的synchronized方法與非synchronized方法

          非synchronized方法不受影響,并行執(zhí)行

          5、兩個(gè)線程訪問同一對(duì)象的不同的synchronized方法

          同一實(shí)例擁有同一把鎖,所以順序執(zhí)行(說明:鎖的是this對(duì)象==同一把鎖)

          6、兩個(gè)線程同時(shí)訪問同一對(duì)象的static的synchronized方法與非static的synchronized方法

          static同步方法是類鎖,非static是對(duì)象鎖,原理上是不同的鎖,所以不受影響,并行執(zhí)行

          7、方法拋出異常后,會(huì)釋放鎖嗎

          會(huì)自動(dòng)釋放鎖,這里區(qū)別Lock,Lock需要顯示的釋放鎖

          3個(gè)核心思想:

          • 一把鎖只能同時(shí)被一個(gè)線程獲取,沒有拿到鎖的線程必須等待(對(duì)應(yīng)1、5的情景)

          • 每個(gè)實(shí)例都對(duì)應(yīng)有自己的一把鎖,不同的實(shí)例之間互不影響;例外:鎖對(duì)象是*.class以及synchronized被static修飾的時(shí)候,所有對(duì)象共用同一把鎖(對(duì)應(yīng)2、3、4、6情景)

          • 無論是方法正常執(zhí)行完畢還是方法拋出異常,都會(huì)釋放鎖(對(duì)應(yīng)7情景)

          補(bǔ)充:

          問題:目前進(jìn)入到被synchronized修飾的方法,這個(gè)方法里邊調(diào)用了非synchronized方法,是線程安全的嗎?

          package?cn.jsonshare.java.base.synchronizedtest;

          /**
          ?*?目前進(jìn)入到被synchronized修飾的方法,這個(gè)方法里邊調(diào)用了非synchronized方法,是線程安全的嗎?
          ?*
          ?*?@author?JSON
          ?*/

          public?class?SynchronizedScene8?{
          ????public?static?void?main(String[]?args)?{
          ????????new?Thread(()?->?{
          ????????????method1();
          ????????}).start();

          ????????new?Thread(()?->?{
          ????????????method1();
          ????????}).start();
          ????}

          ????public?static?synchronized?void?method1()?{
          ????????method2();
          ????}

          ????private?static?void?method2()?{
          ????????System.out.println(Thread.currentThread().getName()?+?"進(jìn)入非Synchronized方法");
          ????????try?{
          ????????????Thread.sleep(3000);
          ????????}?catch?(InterruptedException?e)?{
          ????????????e.printStackTrace();
          ????????}

          ????????System.out.println(Thread.currentThread().getName()?+?"結(jié)束非Synchronized方法");
          ????}
          }

          結(jié)論:這樣是線程安全的

          四、性質(zhì)

          1、可重入

          指的是同一線程的外層函數(shù)獲取鎖之后,內(nèi)層函數(shù)可以直接再次獲取該鎖

          Java典型的可重入鎖:synchronized、ReentrantLock

          好處:避免死鎖,提升封裝性

          粒度:線程而非調(diào)用

          • 情況1:證明同一方法是可重入的

          • 情況2:證明可重入不要求是同一方法

          • 情況3:證明可重入不要求是同一類中的

          2、不可中斷

          一旦這個(gè)鎖被別的線程獲取了,如果我現(xiàn)在想獲得,我只能選擇等待或者阻塞,直到別的線程釋放這個(gè)鎖,如果別的線程永遠(yuǎn)不釋放鎖,那么我只能永遠(yuǎn)的等待下去。

          相比之下,Lock類可以擁有中斷的能力,第一點(diǎn):如果我覺得我等待的時(shí)間太長(zhǎng)了,有權(quán)中斷現(xiàn)在已經(jīng)獲取到鎖的線程執(zhí)行;第二點(diǎn):如果我覺得我等待的時(shí)間太長(zhǎng)了不想再等了,也可以退出。

          五、原理

          1、加解鎖原理(現(xiàn)象、時(shí)機(jī)、深入JVM看字節(jié)碼)

          現(xiàn)象:每一個(gè)類的實(shí)例對(duì)應(yīng)一把鎖,每一個(gè)synchronized方法都必須首先獲得調(diào)用該方法的類的實(shí)例的鎖,方能執(zhí)行,否則就會(huì)阻塞,方法執(zhí)行完成或者拋出異常,鎖被釋放,被阻塞線程才能獲取到該鎖,執(zhí)行。

          獲取和釋放鎖的時(shí)機(jī):內(nèi)置鎖或監(jiān)視器鎖

          package?cn.jsonshare.java.base.synchronizedtest;

          import?java.util.concurrent.locks.Lock;
          import?java.util.concurrent.locks.ReentrantLock;

          /**
          ?*?method1?等價(jià)于?method2
          ?*
          ?*?@author?JSON
          ?*?@date?2019-08-29
          ?*/

          public?class?SynchronizedToLock1?{
          ????Lock?lock?=?new?ReentrantLock();

          ????public?synchronized?void?method1(){
          ????????System.out.println("執(zhí)行method1");
          ????}

          ????public?void?method2(){
          ????????lock.lock();
          ????????try?{
          ????????????System.out.println("執(zhí)行method2");
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}finally?{
          ????????????lock.unlock();
          ????????}
          ????}

          ????public?static?void?main(String[]?args)?{
          ????????SynchronizedToLock1?sl?=?new?SynchronizedToLock1();

          ????????//?method1?等價(jià)于?method2
          ????????sl.method1();
          ????????sl.method2();
          ????}
          }

          深入JVM看字節(jié)碼:

          ...
          monitorenter指令
          ...
          monitorexit指令
          ...

          2、可重入原理(加鎖次數(shù)計(jì)數(shù)器)

          JVM負(fù)責(zé)跟蹤對(duì)象被加鎖的次數(shù)

          線程第一次給對(duì)象加鎖的時(shí)候,計(jì)數(shù)變?yōu)?,每當(dāng)這個(gè)相同的線程在此對(duì)象上再次獲得鎖時(shí),計(jì)數(shù)會(huì)遞增

          每當(dāng)任務(wù)離開時(shí),計(jì)數(shù)遞減,當(dāng)計(jì)數(shù)為0的時(shí)候,鎖被完全釋放

          3、可見性原理(內(nèi)存模型)

          Java內(nèi)存模型

          線程A向線程B發(fā)送數(shù)據(jù)的過程(JMM控制)

          synchronized關(guān)鍵字實(shí)現(xiàn)可見性:

          被synchronized修飾,那么執(zhí)行完成后,對(duì)對(duì)象所做的任何修改都要在釋放鎖之前,都要從線程內(nèi)存寫入到主內(nèi)存,所以主內(nèi)存中的數(shù)據(jù)是最新的。

          六、缺陷

          1、效率低

          1)、鎖的釋放情況少(線程執(zhí)行完成或者異常情況釋放)

          2)、試圖獲得鎖時(shí)不能設(shè)定超時(shí)(只能等待)

          3)、不能中斷一個(gè)正在試圖獲得鎖的線程(不能中斷)

          2、不夠靈活

          加鎖和釋放的時(shí)機(jī)比較單一,每個(gè)鎖僅有單一的條件(某個(gè)對(duì)象),可能是不夠的

          比如:讀寫鎖更靈活

          3、無法預(yù)判是否成功獲取到鎖

          七、常見問題

          1、synchronized關(guān)鍵字注意點(diǎn):

          • 鎖對(duì)象不能為空

          • 作用域不宜過大

          • 避免死鎖

          2、如何選擇Lock和synchronized關(guān)鍵字?

          總結(jié)建議(優(yōu)先避免出錯(cuò)的原則):

          • 如果可以的話,盡量?jī)?yōu)先使用java.util.concurrent各種類(不需要考慮同步工作,不容易出錯(cuò))

          • 優(yōu)先使用synchronized,這樣可以減少編寫代碼的量,從而可以減少出錯(cuò)率

          • 若用到Lock或Condition獨(dú)有的特性,才使用Lock或Condition

          八、總結(jié)

          一句話總結(jié)synchronized:

          JVM會(huì)自動(dòng)通過使用monitor來加鎖和解鎖,保證了同一時(shí)刻只有一個(gè)線程可以執(zhí)行指定的代碼,從而保證線程安全,同時(shí)具有可重入和不可中斷的特性。

          END


          往期推薦

          Serverless:為我們到底帶來了什么

          我們常用的 Integer 內(nèi)部為什么會(huì)去實(shí)現(xiàn) Comparable 接口?

          為什么有些大公司技術(shù)弱爆了?

          Java中New一個(gè)對(duì)象是個(gè)怎么樣的過程?

          一個(gè)被 CEO 逼瘋的技術(shù)負(fù)責(zé)人的檢討書


          離職成為自由開發(fā)者的100天

          星球限時(shí)拼團(tuán)優(yōu)惠進(jìn)行中

          ???????


          星球兩大分享內(nèi)容

          瀏覽 49
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美簧片在线 | 亚洲高清无码播放 | 青青操在线观看视频 | 天堂一区二区三区 | 看免费毛片!!! |