1. 程式人生 > >Java 多執行緒全域性鎖與物件鎖

Java 多執行緒全域性鎖與物件鎖

  我們看一個例子:

class Demo {
    public synchronized void test() {
        System.out.println("test方法開始執行,當前執行緒為:"+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("test方法執行完畢,當前執行緒為:"
+Thread.currentThread().getName()); } } class MyThread implements Runnable { @Override public void run() { Demo demo = new Demo(); demo.test(); } } public class Test { public static void main(String[] args) { MyThread myThread = new MyThread(); new
Thread(myThread,"子執行緒A").start(); new Thread(myThread,"子執行緒B").start(); new Thread(myThread,"子執行緒C").start(); } }

  執行結果:

執行結果

  從執行結果我們可以看出,Demo類提供的test同步方法好像並沒有起作用,這是怎麼一回事。

  實際上,synchronized(this) 以及非 static 的 synchronized 方法,只能防止多個執行緒同時執行同一個物件的同步程式碼塊。即 synchronized 鎖住的是括號裡的物件,而不是程式碼塊

  所以說 synchronized 是一個物件鎖。

  當 synchronized 鎖住一個物件後,別的執行緒如果也想拿到這個物件的鎖,就必須等待這個執行緒執行完成釋放鎖,才能再次給物件加鎖,這樣才能達到執行緒同步的目的。所以即使兩個不同的程式碼塊都要鎖住同一個物件,那麼這兩個程式碼段也不能在多執行緒環境下同時執行,必須等其中一個現將物件鎖釋放掉,另一個才能給物件上鎖。

  所以在上例中,MyThread執行緒類啟動三次也建立了三個Demo類,並且對其呼叫,三個不同的物件進入了同步方法中,所以顯示如上結果。

  當一個執行緒A 進入到同步方法所在的類中,其他執行緒不能進入該類中的其他類中,因為鎖住的是物件。類比:廁所裡有個電視機,某人上廁所時關上了鎖,其他人也不能進來看電視。

  那我們如果想將一段程式碼鎖住,使同時有且只有一個物件能訪問該程式碼塊應該如何操作。

  這種鎖住程式碼塊的的操作叫做全域性鎖,可以通過以下兩種途徑實現:

1.1 鎖住同一個物件

class Demo {
    public void test() {
        // 鎖住進入的方法的物件
        synchronized(this) {
            System.out.println("test方法開始執行,當前執行緒為:"+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("test方法執行完畢,當前執行緒為:"+Thread.currentThread().getName());
        }
    }
}


class MyThread implements Runnable {
    // 為了防止多個執行緒建立多個物件,所以在類中自己建立一個物件
    private Demo demo;
    // 在構造方MyThread時將真正的物件傳入
    public MyThread(Demo demo) {
        this.demo = demo;
    }

    @Override
    public void run() {
        this.demo.test();
    }

}
public class Test {
    public static void main(String[] args) {
        // 實際上,整個程式只有這一個物件
        // 鎖住了該物件就相當於將 Demo類中的test方法程式碼鎖住了,曲線救國實現全域性鎖
        Demo demo = new Demo();
        MyThread myThread = new MyThread(demo);
        new Thread(myThread,"子執行緒A").start();
        new Thread(myThread,"子執行緒B").start();
        new Thread(myThread,"子執行緒C").start();
    }
}

1.2 鎖住整個類

class Demo {
    public void test() {
        // 將 Demo類 作為鎖定的物件,每次只能有一個物件進入該類
        synchronized(Demo.class) {
            System.out.println("test方法開始執行,當前執行緒為:"+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("test方法執行完畢,當前執行緒為:"+Thread.currentThread().getName());
        }
    }
}


class MyThread implements Runnable {

    @Override
    public void run() {
        // 雖然這裡還是存在建立多個物件的問題
        // 但是由於test方法這次鎖住了整個類,所以同時有且僅有一個物件能夠進入Demo類中
        Demo demo = new Demo();
        demo.test();
    }

}
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        new Thread(myThread,"子執行緒A").start();
        new Thread(myThread,"子執行緒B").start();
        new Thread(myThread,"子執行緒C").start();
    }
}

  執行結果:

執行結果

  當然,使用靜態同步方法也可以實現鎖住整個類的效果。

public static synchronized test() {
    // statement
}