歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java synchronized關鍵字用法

本篇隨筆主要介紹 java 中 synchronized 關鍵字常用法,主要有以下四個方面:

1、實例方法同步

2、靜態方法同步

3、實例方法中同步塊

4、靜態方法中同步塊

我覺得在學習synchronized關鍵字之前,我們首先需要知道以下一點:Java 中每個實例對象對應一把鎖且每個實例對象只有一把鎖,synchronized 關鍵字是通過對相應的實例對象加鎖來實現同步功能的。

一、實例方法中使用 synchronized 加鎖

實例方法中默認被加鎖的對象是調用此方法的實例對象。

class ImmutableValue {
    public synchronized void comeIn() throws InterruptedException{
        System.out.println(Thread.currentThread().getName() + ": start");
        Thread.sleep(5000);
        System.out.println(Thread.currentThread().getName() + ": finish");
    }
    public  void synchronized comeInIn() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + ": start");
        Thread.sleep(5000);
        System.out.println(Thread.currentThread().getName() + ": finish");
    }
}
public class TestImmutableValue {
    public static void main(String[] args) {
        ImmutableValue im = new ImmutableValue();
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    im.comeIn();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
           
        }, "t1");
        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    im.comeInIn();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
           
        }, "t2");
        t1.start();
        t2.start();
    }
}

在上面的代碼中創建了兩個線程並分別命名為 t1, t2。調用了同一個對象 im 的兩個同步方法 comeIn 和 comeInIn, 執行結果如下:

在 t1 線程開始執行後,即使 t1 線程睡眠了5s,線程 t2 中的 comeInIn 方法仍然沒有得到執行。這是因為 t1 線程先執行的 comeIn 方法,持有了對象 im 的鎖,且 comeIn 方法並沒有執行完,對象 im 的鎖沒有被釋放,所以 comeInIn 方法無法對對象 im 加鎖,就無法繼續執行,只能等到 t1 線程中的 comeIn 方法執行完畢,釋放對象 im 的鎖,comeInIn 方法才能繼續執行。

但是如果 t1 線程調用的是對象 im 的 comeIn 方法,而 t2 線程調用的是我們聲明的另外一個  ImmutableValue 對象 im2 對象的 comeInIn 方法,則這兩個方法的執行是互不影響的。因為 t1 線程的 comeIn 方法要獲得 im 對象的鎖,而 t2 線程要獲得的是 im2 對象的鎖,兩個鎖並不是同一個鎖(Java中每個實例對象都有且只有一個鎖),所以這兩個方法執行互不影響。

二、靜態方法中使用 synchronized 加鎖

靜態方法中默認被加鎖的對象是此靜態方法所在類的 class 對象。

class staticMethodSynchronized {
    public static synchronized void method1() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + ": start");
        Thread.sleep(5000);
        System.out.println(Thread.currentThread().getName() + ": finish");
    }
    public static synchronized void method2() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + ": start");
        Thread.sleep(5000);
        System.out.println(Thread.currentThread().getName() + ": finish");
    }
}
public class TestStaticClassSynchronized {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    staticMethodSynchronized.method1();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
           
        }, "t1");
        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    staticMethodSynchronized.method2();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
           
        }, "t2");
        t1.start();
        t2.start();
    }
}

在上述代碼中創建了兩個線程並命名為 t1,t2。 t1,t2 線程調用了 staticMethodSynchronized 類的兩個靜態同步方法 method1 和 method2。執行結果如下:

在 t1 線程開始執行後,即使 t1 線程睡眠了5s,線程 t2 中的 method2 方法仍然沒有得到執行。這是因為 t1 線程先執行的 method1 方法,持有了staticMethodSynchronized 類對象的鎖,且 method1 方法並沒有執行完,staticMethodSynchronized 類對象的鎖沒有被釋放,所以 comeInIn 方法無法對staticMethodSynchronized 類對象加鎖,就無法繼續執行,只能等到 t1 線程中的 method1 方法執行完畢,釋放 staticMethodSynchronized 類對象的鎖,method2 方法才能繼續執行。

三、實例方法中使用 synchronized 關鍵字制造同步塊

同步塊中默認被加鎖的對象是此同步塊括號聲明中包含的對象。

class ImmutableValue {
    public synchronized void comeIn() throws InterruptedException{
        System.out.println(Thread.currentThread().getName() + ": start");
        Thread.sleep(5000);
        System.out.println(Thread.currentThread().getName() + ": finish");
    }
    public void comeInIn() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + ": start");
        synchronized(this) {
           
        }
        System.out.println(Thread.currentThread().getName() + ": finish");
    }
}
public class TestImmutableValue {
    public static void main(String[] args) {
        ImmutableValue im = new ImmutableValue();
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    im.comeIn();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
           
        }, "t1");
        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    im.comeInIn();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
           
        }, "t2");
        t1.start();
        t2.start();
    }
}

由以上代碼可以看到: 在 comeInIn 方法中,運用  synchronized(this) 制造同步塊,要執行同步塊內的代碼,就必須獲得 this 對象的鎖(調用 comeInIn 方法的對象)。

執行結果可能為:

由此執行結果可見:t1 線程先執行了 comeIn 方法,獲得了對象 im 的鎖,之後由於 t1 線程進入睡眠狀態,t2 線程得到運行,開始執行 comeInIn 方法,當執行到同步代碼塊時發現對象 im 已被加鎖,無法繼續執行。t1 線程睡眠結束之後繼續執行,結束後釋放對象 im 的鎖,t2 線程才能繼續執行。

四、靜態方法中使用 synchronized 關鍵字制造同步塊

同步塊中默認被加鎖的對象是此同步塊括號聲明中包含的對象。

class staticMethodSynchronized {
    private static final Object OBJ = new Object();
    public static void method1() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + ": start");
        synchronized(OBJ) {
            System.out.println(Thread.currentThread().getName() + ": 獲得鎖");
            System.out.println(Thread.currentThread().getName() + ": 釋放鎖");
        }
        System.out.println(Thread.currentThread().getName() + ": finish");
    }
    public static void method2() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + ": start");
        synchronized(OBJ) {
            System.out.println(Thread.currentThread().getName() + ": 獲得鎖");
            System.out.println(Thread.currentThread().getName() + ": 釋放鎖");
        }
        System.out.println(Thread.currentThread().getName() + ": finish");
    }
}
public class TestStaticClassSynchronized {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    staticMethodSynchronized.method1();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
           
        }, "t1");
        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    staticMethodSynchronized.method2();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
           
        }, "t2");
        t1.start();
        t2.start();
    }
}

在上述代碼中,兩個靜態方法中的同步塊都要獲得對象 OBJ 的鎖才能繼續向下執行,執行結果可能如下:

若 t1 線程先獲得鎖,則必須等到 t1 釋放鎖之後,t2 線程中同步代碼塊及其之後的代碼才能繼續執行,t2 線程先獲得鎖,t1 線程同理。

總之,我認為我們只需抓住一點:Java 中每個實例對象對應一把鎖且每個實例對象只有一把鎖,synchronized 關鍵字是通過對相應的實例對象加鎖來實現同步功能的(靜態方法為對相應的 class 對象加鎖)。在執行 synchronized 方法或 synchronized 同步塊之前,我們只需判斷其需要獲得的對象的鎖是否可獲得,就可判斷此方法或同步塊是否可得到執行。

Copyright © Linux教程網 All Rights Reserved