Java:多線程,線程同步,synchronized關鍵字的用法(同步代碼塊、非靜態同步方法、靜態同步方法)

關於線程的同步,可使用synchronized關鍵字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock對象。本文探討synchronized關鍵字。java

synchronized關鍵字能夠修飾方法,能夠修飾代碼塊,但不能修飾構造器、屬性等。併發

對synchronized(this)的一些理解

  1. 當兩個併發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程獲得執行。另外一個線程必須等待當前線程執行完這個代碼塊之後才能執行該代碼塊。 
  2. 當一個線程訪問object的一個synchronized(this)同步代碼塊時,其餘線程對object中全部其它synchronized(this)同步代碼塊的訪問將被阻塞。
  3. 然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另外一個線程仍然能夠訪問該object中的除synchronized(this)同步代碼塊之外的部分。
  4. 第三個例子一樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就得到了這個object的對象鎖。結果,其它線程對該object對象全部同步代碼部分的訪問都被暫時阻塞。
  5. 以上規則對其它對象鎖一樣適用。

一:synchronized同步代碼塊

package com.clzhang.sample.thread;

public class SyncThread1 implements Runnable {
    private Integer key = 0;

    @Override
    public void run() {
        // key是Integer對象(注意不是int,由於int不是對象)
        // 線程進入下面同步代碼以前,須要先獲取key的鎖。
        // 須要結果是key實現自增加,若是沒有同步塊,則可能會出現重複key值的現象
        synchronized (key) {
            key++;
            
            System.out.println(Thread.currentThread().getName() + ":" + key);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
        }
    }

    public static void main(String[] args) {
        SyncThread1 st = new SyncThread1();
        
        for(int i=0; i<10; i++) {
            new Thread(st, "Thread" + i).start();
        }
    }
}

輸出:ide

Thread1:2
Thread3:3
Thread5:4
Thread7:5
Thread0:2
Thread2:7
Thread9:6
Thread4:8
Thread6:9
Thread8:10this

二:synchronized同步方法

同步方法分靜態和非靜態兩種。靜態方法則必定會同步,非靜態方法需在單例模式才生效,推薦用靜態方法(不用擔憂是否單例)。spa

2.1 非靜態方法同步示範

package com.clzhang.sample.thread;

// 若是是同步方法,則分靜態和非靜態兩種。
// 靜態方法則必定會同步,非靜態方法需在單例模式才生效,推薦用靜態方法(不用擔憂是否單例)。
public class SyncThread2 implements Runnable {
    private Integer key = 0;

    // 此示範爲非靜態方法同步
    public synchronized Integer getKey() {
        key++;

        return key;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":" + getKey());
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
        }
    }

    public static void main(String[] args) {
        // 非靜態方法同步,須要啓動單例模式
        SyncThread2 st = new SyncThread2();
        for (int i = 0; i < 10; i++) {
            new Thread(st, "Thread" + i).start();
        }
    }
}

輸出:線程

Thread0:1
Thread1:3
Thread2:2
Thread3:5
Thread5:6
Thread7:7
Thread9:8
Thread6:9
Thread8:10
Thread4:4code

2.2 靜態方法同步示範

package com.clzhang.sample.thread;

// 若是是同步方法,則分靜態和非靜態兩種。
// 靜態方法則必定會同步,非靜態方法需在單例模式才生效,推薦用靜態方法(不用擔憂是否單例)。
public class SyncThread3 implements Runnable {
    private static Integer key = 0;

    // 此示範爲靜態方法同步
    public synchronized static Integer getKey() {
        key++;

        return key;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":" + getKey());
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
        }
    }

    public static void main(String[] args) {
        
        // 若是用靜態方法實現同步,則能夠生成對象的多個實例
        for (int i = 0; i < 10; i++) {
            SyncThread3 st = new SyncThread3();
            new Thread(st, "Thread" + i).start();
        }
    }
}

輸出:對象

Thread3:3
Thread1:1
Thread0:2
Thread5:4
Thread7:5
Thread9:6
Thread2:7
Thread8:10
Thread6:9
Thread4:8
blog

總結

一、不管是同步代碼塊仍是同步方法,必須得到對象鎖纔可以進入同步代碼塊或者同步方法進行操做。get

二、若是採用方法級別的同步,對象鎖爲方法所在的對象;若是是靜態同步方法,對象鎖爲方法所在的類(惟一)。

三、對於代碼塊,對象鎖即指synchronized(object)中的object。 

相關文章
相關標籤/搜索