首頁  >  文章  >  Java  >  一起看看synchronized的4個特性

一起看看synchronized的4個特性

青灯夜游
青灯夜游轉載
2020-07-07 16:15:502985瀏覽

一起看看synchronized的4個特性

1. synchronized鎖定重入

##1.1 介紹



一起看看synchronized的4個特性


關鍵字synchronized擁有鎖定重入的功能,也就是在使用synchronized時,當一個執行緒得到一個物件鎖定後,再次請求此物件鎖定時是可以再次取得該物件的鎖定的。這說明在一個synchronized方法/區塊內部呼叫本類別的其他synchronized方法/區塊時,是永遠可以得到鎖定的。

例如:

public class Service1 {

    public synchronized void method1(){
        System.out.println("method1");
        method2();
    }

    public synchronized void method2(){
        System.out.println("method2");
        method3();
    }

    public synchronized void method3(){
        System.out.println("method3");
    }

}
public class MyThread extends Thread {

    @Override
    public void run(){
        Service1 service1 = new Service1();
        service1.method1();
    }


    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

運行結果如下:


☹ 看到這個結果的時候是茫然了,怎麼就證明是
可重入鎖

的了呢?

➤ 「可重入鎖」的概念是:自己可以再次取得自己的內部鎖,例如有1個執行緒獲得了某個物件的鎖,此時這個物件鎖還沒有釋放,當其再次想要取得這個物件的鎖的時候還是可以取得的,如果不可鎖重入的話,就會造成死鎖。 ➤ 「可重入鎖定」的最大作用就是避免死鎖

#1.2 分析

##我們知道,在程式中,是無法明確釋放對同步監視器的鎖的,而會在以下幾個情況下釋放鎖:

① 當前執行緒的同步方法、程式碼區塊執行結束的時候釋放

② 當前執行緒在同步方法、同步程式碼區塊遇到break、return終止程式碼區塊或方法的時候釋放
③ 出現未處理的error或exception導致異常結束的時候釋放一起看看synchronized的4個特性④ 程式執行了同步對象wait方法,當前執行緒暫停,釋放鎖

那麼,在上面的程式中,當執行緒進入同步方法method1時獲得Service1的物件鎖,但是在執行method1的時候呼叫了同步方法method2,按照正常情況,在執行同步方法method2同樣需要取得物件鎖,但是根據上面釋放鎖的條件,此時method1的物件鎖還沒有釋放,此時就會造成死鎖,無法繼續執行method2。但透過上面程式碼的執行結果來看,能夠正常執行method2和method3,這就說明,

在一個Synchronized修飾的方法或程式碼區塊的內部呼叫本類別的其他Synchronized修飾的方法或程式碼區塊時,是永遠可以得到鎖的

1.3 父子可繼承性


#可重入鎖定支援在父子類別繼承的環境中,範例程式碼如下:

public class Service2 {
    public int i = 10;
    public synchronized void mainMethod(){
        i--;
        System.out.println("main print i="+i);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Service3 extends Service2 {

    public synchronized void subMethod(){
        try{
            while (i>0){
                i--;
                System.out.println("sub print i= "+i);
                Thread.sleep(100);
                this.mainMethod();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public class MyThread extends Thread {

    @Override
    public void run(){
        Service3 service3 = new Service3();
        service3.subMethod();
    }


    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}
執行結果如下:
一起看看synchronized的4個特性
此程式說明,當存在父子類別繼承關係時,子類別完全可以透過「可重入鎖定」呼叫父類別的同步方法。

2. 出現異常,鎖定會自動釋放

一起看看synchronized的4個特性
#當一個執行緒執行的程式碼出現異常時,其所持有的鎖定會自動釋放。
驗證程式碼如下:

public class Service4 {

    public synchronized void testMethod(){
        if(Thread.currentThread().getName().equals("a")){
            System.out.println("ThreadName= "+Thread.currentThread().getName()+" run beginTime="+System.currentTimeMillis());
            int i=1;
            while (i == 1){
                if((""+Math.random()).substring(0,8).equals("0.123456")){
                    System.out.println("ThreadName= "+Thread.currentThread().getName()+" run exceptionTime="+System.currentTimeMillis());
                  //Integer.parseInt("a");
                }
            }
        }else{
            System.out.println("Thread B run time= "+System.currentTimeMillis());
        }
    }
}
public class ThreadA extends Thread{

    private Service4 service4;

    public ThreadA(Service4 service4){
        this.service4 = service4;
    }

    @Override
    public void run(){
        service4.testMethod();
    }
}
public class ThreadB extends Thread{

    private Service4 service4;

    public ThreadB(Service4 service4){
        this.service4 = service4;
    }

    @Override
    public void run(){
        service4.testMethod();
    }
}
public class Main {
    public static void main(String[] args) {
        try {
            Service4 service4 = new Service4();

            ThreadA a = new ThreadA(service4);
            a.setName("a");
            a.start();

            Thread.sleep(500);

            ThreadB b = new ThreadB(service4);
            b.setName("b");
            b.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

注意Service4類​​別中Integer.parseInt(“a”);此時處於註解狀態,執行結果如下:

由於a執行緒沒有錯誤,while(true),此時a執行緒處於無限循環狀態,鎖一直被a佔用,b執行緒無法取得鎖,即無法執行b執行緒。


將Service4類​​別中

Integer.parseInt(“a”);

解開註釋,執行的結果如下:
一起看看synchronized的4個特性
##  

當a執行緒發生錯誤時,b執行緒取得鎖從而執行,由此可見,當方法出現異常時,鎖自動釋放。 ############3. 將任意物件作為監視器#############java支援對「任意物件」作為「物件監視器」來實現同步的功能。這個「任意物件」大多是實例變數及方法的參數,使用格式為synchronized(非this物件x)同步程式碼區塊。 ###範例程式碼如下:###
public class StringLock {

    private String lock = "lock";

    public void method(){
        synchronized (lock){
            try {
                System.out.println("当前线程: "+Thread.currentThread().getName() + "开始");
                Thread.sleep(1000);
                System.out.println("当前线程: "+Thread.currentThread().getName() + "结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        final StringLock stringLock = new StringLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                stringLock.method();
            }
        },"t1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                stringLock.method();
            }
        },"t2").start();
    }
}
### 運行結果如下: #########鎖定非this物件有一定的優點:如果在一個類別中有很多個synchronized方法,這時雖然能實現同步,但會受到阻塞,所以影響運行效率;但如果使用同步程式碼區塊鎖非this對象,則synchronized(非this)程式碼區塊中的程式與同步方法是異步的,不予其他鎖this同步方法爭搶this鎖,則可大幅提高運作效率。 ###

4. 同步不具有繼承性

父類別的同步方法,在子類別中重寫後不加同步關鍵字,是不會同步的,所以還得在子類別的方法中加入synchronized關鍵字。

推薦學習:Java影片教學

以上是一起看看synchronized的4個特性的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除