溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Synchronized怎么用

發(fā)布時間:2021-11-17 11:01:40 來源:億速云 閱讀:206 作者:小新 欄目:大數(shù)據(jù)

小編給大家分享一下Synchronized怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

synchronized可以保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區(qū),同時它還可以保證共享變量的內(nèi)存可見性

Java中每一個對象都可以作為鎖,這是synchronized實現(xiàn)同步的基礎(chǔ): 
1. 普通同步方法,鎖是當前實例對象 
2. 靜態(tài)同步方法,鎖是當前類的class對象 
3. 同步方法塊,鎖是括號里面的對象

synchronized 獲取的鎖,在方法拋出異常的時候會自動解鎖 

實現(xiàn)原理

利用javap工具查看生成的class文件信息來分析Synchronize的實現(xiàn) 

Synchronized怎么用
從上面可以看出,同步代碼塊是使用monitorenter和monitorexit指令實現(xiàn)的,同步方法(在這看不出來需要看JVM底層實現(xiàn))依靠的是方法修飾符上的ACC_SYNCHRONIZED實現(xiàn)。 
同步代碼塊:monitorenter指令插入到同步代碼塊的開始位置,monitorexit指令插入到同步代碼塊的結(jié)束位置,JVM需要保證每一個monitorenter都有一個monitorexit與之相對應(yīng)。任何對象都有一個monitor與之相關(guān)聯(lián),當且一個monitor被持有之后,他將處于鎖定狀態(tài)。線程執(zhí)行到monitorenter指令時,將會嘗試獲取對象所對應(yīng)的monitor所有權(quán),即嘗試獲取對象的鎖; 
同步方法:synchronized方法則會被翻譯成普通的方法調(diào)用和返回指令如:invokevirtual、areturn指令,在VM字節(jié)碼層面并沒有任何特別的指令來實現(xiàn)被synchronized修飾的方法,而是在Class文件的方法表中將該方法的access_flags字段中的synchronized標志位置1,表示該方法是同步方法并使用調(diào)用該方法的對象或該方法所屬的Class在JVM的內(nèi)部對象表示Klass做為鎖對象。

同步方法和同步代碼塊


同步方法就是在方法前加關(guān)鍵字synchronized,然后被同步的方法一次只能有一個線程進入,其他線程等待。
而同步塊則是在方法內(nèi)部使用大括號使得一個代碼塊得到同步。同步塊會有一個同步的”目標“,使得同步塊更加靈活一些(同步塊可以通過”目標“決定需要鎖定的對象)。一般情況下,如果此”目標“為this,那么同步方法和同步塊沒有太大的區(qū)別。

對象鎖和類鎖的區(qū)別

synchronized 加到 static 方法前面是給class 加鎖,即類鎖;而synchronized 加到非靜態(tài)方法前面是給對象上鎖。這兩者的區(qū)別我用代碼來演示下:

對象鎖和類鎖是不同的鎖,所以多個線程同時執(zhí)行這2個不同鎖的方法時,是異步的。
在Task2 中定義三個方法 doLongTimeTaskA和doLongTimeTaskB是類鎖,而doLongTimeTaskC是對象鎖。

public class Task2 {

    public synchronized static void doLongTimeTaskA() {
        System.out.println("name = " + Thread.currentThread().getName() + ", begain");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("name = " + Thread.currentThread().getName() + ", end");
    }

    public synchronized static void doLongTimeTaskB() {
        System.out.println("name = " + Thread.currentThread().getName() + ", begain");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("name = " + Thread.currentThread().getName() + ", end");
    }

    public synchronized void doLongTimeTaskC() {

        System.out.println("name = " + Thread.currentThread().getName() + ", begain");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("name = " + Thread.currentThread().getName() + ", end");

    }

三個線程的代碼如下:

class ThreadA extends Thread{

    private Task2 mTask2;

    public ThreadA(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        mTask2.doLongTimeTaskA();
    }
}

class ThreadB extends Thread{

    private Task2 mTask2;

    public ThreadB(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        mTask2.doLongTimeTaskB();
    }
}

class ThreadC extends Thread{

    private Task2 mTask2;

    public ThreadC(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        mTask2.doLongTimeTaskC();
    }
}

main函數(shù)中執(zhí)行代碼如下:

        Task2 mTask2 = new Task2();
        ThreadA ta = new ThreadA(mTask2);
        ThreadB tb = new ThreadB(mTask2);
        ThreadC tc = new ThreadC(mTask2);

        ta.setName("A");
        tb.setName("B");
        tc.setName("C");

        ta.start();
        tb.start();
        tc.start();
}
執(zhí)行的結(jié)果如下:

name = A, begain, time = 1487311199783
name = C, begain, time = 1487311199783
name = C, end, time = 1487311200784
name = A, end, time = 1487311200784
name = B, begain, time = 1487311200784
name = B, end, time = 1487311201784

可以看出由于 doLongTimeTaskA和doLongTimeTaskB都是類鎖,即同一個鎖,所以 A和B是按順序執(zhí)行,即同步的。而C是對象鎖,和A/B不是同一種鎖,所以C和A、B是 異步執(zhí)行的。(A、B、C代指上面的3中方法)。

我們知道對象鎖要想保持同步執(zhí)行,那么鎖住的必須是同一個對象。下面就修改下上面的來證明:

Task2.java不變,修改ThreadA 和 ThreadB 如下:

class ThreadA extends Thread{

    private Task2 mTask2;

    public ThreadA(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        mTask2.doLongTimeTaskC();
    }
}

class ThreadB extends Thread{

    private Task2 mTask2;

    public ThreadB(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        mTask2.doLongTimeTaskC();
    }
}

main方法如下:

 Task2 mTaska = new Task2();
 Task2 mTaskb = new Task2();
 ThreadA ta = new ThreadA(mTaska );
 ThreadB tb = new ThreadB(mTaskb );


 ta.setName("A");
 tb.setName("B");

 ta.start();
 tb.start();

結(jié)果如下:

name = A, begain, time = 1487311905775
name = B, begain, time = 1487311905775
name = B, end, time = 1487311906775
name = A, end, time = 1487311906775

從結(jié)果看來,對象鎖鎖的對象不一樣,分別是mTaska , mTaskb,所以線程A和線程B調(diào)用 doLongTimeTaskC 是異步執(zhí)行的。

但是,類鎖可以對類的所有對象的實例起作用。只需修改ThradA 
和 ThreadB,main 方法不做改變,修改如下:

class ThreadA extends Thread{

    private Task2 mTask2;

    public ThreadA(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        //mTask2.doLongTimeTaskC();
        mTask2.doLongTimeTaskA();
    }
}

class ThreadB extends Thread{

    private Task2 mTask2;

    public ThreadB(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
       //mTask2.doLongTimeTaskC();
        mTask2.doLongTimeTaskA();
    }
}

結(jié)果如下:

name = A, begain, time = 1487312239674
name = A, end, time = 1487312240674
name = B, begain, time = 1487312240674
name = B, end, time = 1487312241674

可以看出 在線程A執(zhí)行完doLongTimeTaskA方法后,線程B才會獲得該類鎖接著去執(zhí)行doLongTimeTaskA。也就是說,類鎖對所有的該類對象都能起作用。

總結(jié): 
1. 如果多線程同時訪問同一類的 類鎖(synchronized 修飾的靜態(tài)方法)以及對象鎖(synchronized 修飾的非靜態(tài)方法)這兩個方法執(zhí)行是異步的,原因:類鎖和對象鎖是2中不同的鎖。 
2. 類鎖對該類的所有對象都能起作用,而對象鎖不能。

synchronized可重入鎖的實現(xiàn)

之前談到過,每個鎖關(guān)聯(lián)一個線程持有者和一個計數(shù)器。當計數(shù)器為0時表示該鎖沒有被任何線程持有,那么任何線程都都可能獲得該鎖而調(diào)用相應(yīng)方法。當一個線程請求成功后,JVM會記下持有鎖的線程,并將計數(shù)器計為1。此時其他線程請求該鎖,則必須等待。而該持有鎖的線程如果再次請求這個鎖,就可以再次拿到這個鎖,同時計數(shù)器會遞增。當線程退出一個synchronized方法/塊時,計數(shù)器會遞減,如果計數(shù)器為0則釋放該鎖。

以上是“Synchronized怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI