【JAVA書單】-《JAVA多線程核心技術》-第四章 Lock的使用

此文是對《JAVA多線程編程核心技術》的一點總結,如果想要了解具體細節可以去看原書。

第四章 Lock的使用

lock和synchronized的區別

  • 用法:
    synchronized:在需要同步的對象中加入此控制,synchronized可以加在方法上,也可以加在特定代碼塊中,括號中表示需要鎖的對象。

    lock:需要顯示指定起始位置和終止位置。一般使用ReentrantLock類做爲鎖,多個線程中必須要使用一個ReentrantLock類做爲對象才能保證鎖的生效。且在加鎖和解鎖處需要通過lock()和unlock()顯示指出。所以一般會在finally塊中寫unlock()以防死鎖。

  • 性能:
    synchronized是託管給JVM執行的,而lock是java寫的控制鎖的代碼。在Java1.5中,synchronize是性能低效的。因爲這是一個重量級操作,需要調用操作接口,導致有可能加鎖消耗的系統時間比加鎖以外的操作還多。相比之下使用Java提供的Lock對象,性能更高一些。但是到了Java1.6,發生了變化。synchronize在語義上很清晰,可以進行很多優化,有適應自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖等等。導致在Java1.6上synchronize的性能並不比Lock差。官方也表示,他們也更支持synchronize,在未來的版本中還有優化餘地。

  • 用途
    synchronized原語和ReentrantLock在一般情況下沒有什麼區別,但是在非常複雜的同步應用中,請考慮使用ReentrantLock

公平鎖與非公平鎖:

公平鎖表示線程獲取鎖的順序是按照線程加鎖的順序來分配的,即先來先得的FIFO順序。而非公平鎖就是一種獲取鎖的搶佔機制,是隨機獲得鎖的,和公平鎖不一樣的就是先來的不一定先得到鎖,這個方式可能造成某些線程一直拿不到鎖。從這個角度講,synchronized其實就是一種非公平鎖。

ReentrantLock類的使用

在 Java 多線程中, 可以使用 synchronized 關鍵字來實現多線程之間同步互斥, 但在 JDK 1.5 中新增加了 ReentrantLock 類也能達到同樣的效果, 並且在擴展功能上也更加強大, 比如具有嗅探鎖定, 多路分支通知, 公平鎖和非公平鎖等(默認)功能, 而且在使用上也比 synchronized 更加的靈活

public class MyService {

    private Lock lock = new ReentrantLock();

    public void testMethod() {
    try{
    		 lock.lock();
        	for (int i = 0; i < 10; i++){
            	System.out.println("ThreadName=" + Thread.currentThread().getName() + (" " + (i + 1)));
        	}
    	)catch (InterruptedException e) {
           e.printStackTrace();
       } finally {
       		lock.unlock();
       }
    }
}
public class MyThread extends Thread {

    private MyService myService;
    public MyThread(MyService myService) {
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.testMethod();
    }
}

public static void main(String[] args) throws IOException, InterruptedException {

        MyService myService = new MyService();

        MyThread myThreadA = new MyThread(myService);
        MyThread myThreadB = new MyThread(myService);
        MyThread myThreadC = new MyThread(myService);
        MyThread myThreadD = new MyThread(myService);
        MyThread myThreadE = new MyThread(myService);

        myThreadA.start();
        myThreadB.start();
        myThreadC.start();
        myThreadD.start();
        myThreadE.start();

    }

從運行結果來看, 當前線程打印完畢之後將鎖進行釋放, 其他的線程纔可以繼續打印. 線程打印的數據是分組打印, 因爲當前線程已經持有鎖, 但線程之間打印的順序是隨機的.

使用 Condition 實現等待/通知

關鍵字 synchronized 與 wait() 和 notify() / notifyall() 方法結合可以實現等待/通知模式, 只不過在使用時, 調用 notify() 方法 JVM 會隨機選擇一個 WAITNG 狀態的線程來執行.

而使用 Condition 則可以更加靈活, 可以實現 “選擇性通知”, 可以指定的選擇喚醒哪些線程, 哪些線程繼續等待.

public class MyService {
    private Lock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();
    public void await() {
        try {
            lock.lock();
            System.out.println(" await時間爲" + System.currentTimeMillis());
            condition.await();
        }catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void signal() {
        try {
            lock.lock();
            System.out.println("sigal時間爲:" + System.currentTimeMillis());
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}
public class ZmsThread extends Thread{
    private MyService service;

    public ZmsThread(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.await();
    }
}
public class ZmsRun {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        ZmsThread a = new ZmsThread(myService);
        a.start();
        Thread.sleep(3000);
        myService.signal();
    }
}
Object類與Condition類
  • Object類的wait方法相當於Condition類的await方法
  • Object類的notify方法相當於Condition類的signal方法
  • Object類的notifyAll方法相當於Condition類的signalAll方法
常用方法

int getHoldCount() 查詢調用 lock() 方法的次數.

final int getQueueLength() 估計等待鎖的線程數. 比如有5個線程, 1個線程首先執行 await() 方法, 那麼在調用此方法後返回值是4, 說明有4個線程同時在等待lock的釋放.

int getWaitQueueLength(Condition condition) 返回與此鎖相關聯給定條件等待的線程數的估計. 比如有5個線程, 每個線程都執行了同一個 condition 對象的 await() 方法, 則調用此方法時返回的值是5.

final boolean hasQueuedThreads() 判斷是否有線程等待此鎖.

final boolean hasQueuedThread(Thread thread) 判斷指定線程是否等待獲取此鎖.

boolean hasWaiters(Condition condition) 判斷線程有沒有調用 await() 方法.

void lockInterruptibly() throws InterruptedException 獲取鎖, 除非當前線程爲interrupted

ReentrantReadWriteLock類

類ReentrantLock具有完全互斥排他的效果,這樣做雖然保持了實例變量的線程安全性,但效率是非常低下的。所以JDK中提供了一種讀寫鎖ReentrantReadWriteLock類,使用它可以加快運行效率,在不需要操作實例變量的方法中

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章