1. 程式人生 > >Java synchronized多執行緒互斥技術

Java synchronized多執行緒互斥技術

在java中,synchronized是用來控制執行緒同步的,既為了讓一段程式碼不允許多個執行緒同時訪問,需要排隊一個一個執行,就像我們生活中排隊在公共電話亭打電話一樣,一個人打好電話出來,另外個人才可以進去打電話
問題1:描述了synchronized物件鎖的問題
問題2:描述了static靜態方法加上synchronized的問題

問題1

程式碼

不要認為加上synchronized就萬事大吉了,看下下面一段程式碼


public class DemoTest01 {
    public static void main(String[] args) {
        final
OuterPuter puter = new OuterPuter(); //第一個執行緒 new Thread(new Runnable() { @Override public void run() { while (true) { puter.outerPut1("ABCD"); } } }).start(); //第二個執行緒 new Thread(new
Runnable() { @Override public void run() { while (true) { puter.outerPut1("我愛程式設計"); } } }).start(); } } class OuterPuter { public void outerPut1(String name) { // 將名字一個字一個字的打印出來 synchronized
(name) {//這段程式碼被要求每次只能一個執行緒進行執行 for (int i = 0; i < name.length(); i++) { System.out.print(name.charAt(i)); } // 換行 System.out.println(); } } }

執行結果

這裡寫圖片描述
從結果可以看出,並不是我們想要的結果,我們要的結果是:每行要麼是ABCD,要麼是我愛程式設計
從同步程式碼塊synchronized可以看出,後面的物件name,在兩次執行緒執行的時候,物件不是同一個物件我(只有在鎖物件為同一個鎖),既不是同一個鎖,下面我們進行優化

優化方案1

class OuterPuter {
    public void outerPut1(String name) {
        // 將名字一個字一個字的打印出來
        synchronized (this) {//這裡所物件改成this,即可
            for (int i = 0; i < name.length(); i++) {
                System.out.print(name.charAt(i));
            }
            // 換行
            System.out.println();
        }
    }

將鎖物件改成this後,因為我們建立物件是線上程開啟前,確保物件的唯一,但是千萬不能放到兩個執行緒的run方法裡,因為放到run方法裡,物件建立了兩次,鎖物件不是唯一的了

優化方案2

class OuterPuter {
    public synchronized void outerPut1(String name) {
        for (int i = 0; i < name.length(); i++) {
            System.out.print(name.charAt(i));
        }
        // 換行
        System.out.println();
    }

直接在執行的方法上加上synchronized ,其實他的物件鎖也是this,所以也可以實現兩個執行緒互斥

問題2

程式碼

看下下面一段程式碼,是否能夠實現執行緒互斥


public class DemoTest01 {
    public static void main(String[] args) {
        final OuterPuter puter = new OuterPuter();
        // 第一個執行緒
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    puter.outerPut2("ABCD");
                }
            }
        }).start();
        // 第二個執行緒
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {

                    OuterPuter.outerPut3("我愛程式設計");
                }
            }
        }).start();
    }
}
class OuterPuter {

    public synchronized void outerPut2(String name) {
        for (int i = 0; i < name.length(); i++) {
            System.out.print(name.charAt(i));
        }
        // 換行
        System.out.println();
    }
    //靜態方法
    public static synchronized void outerPut3(String name) {
        // 將名字一個字一個字的打印出來
        for (int i = 0; i < name.length(); i++) {
            System.out.print(name.charAt(i));
        }
        // 換行
        System.out.println();
    }
}

執行結果:

這裡寫圖片描述
下面我們分析下原因:
這裡有另外個知識點:
因為outerPut3方法前面加了static修飾符,靜態方法裡是無法使用this的,他的鎖物件不是this,而是類的Class物件,所以我們需要對程式碼進行優化

優化方案:

class OuterPuter {
    //非靜態方法
    public  void outerPut2(String name) {
        //把鎖物件也改成類的Class物件
        synchronized (OuterPuter.class) {
            for (int i = 0; i < name.length(); i++) {
                System.out.print(name.charAt(i));
            }
            // 換行
            System.out.println();
        }

    }
    //靜態方法
    public static synchronized void outerPut3(String name) {
        // 將名字一個字一個字的打印出來
        for (int i = 0; i < name.length(); i++) {
            System.out.print(name.charAt(i));
        }
        // 換行
        System.out.println();
    }
}

將outerPut2下的程式碼塊也用類的Class物件鎖,這樣可以保證兩個方法體的鎖物件都是同一個,就可以實現了同步程式碼塊的功能,實現了執行緒之間互斥