java ReentranLock鎖

一、效果和synchronized同樣,均可以同步執行,lock方法得到鎖,unlock方法釋放鎖java

使用示例:ide

package com.test;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
    
    private Lock lock = new ReentrantLock();
    public void methodA() {
        try {
            lock.lock();
            System.out.println("methodA begin ThreadName = " + Thread.currentThread().getName() + "time=" + System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println("methodA end ThreadName = " + Thread.currentThread().getName() + "time=" + System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void methodB() {
        try {
            lock.lock();
            System.out.println("methodB begin ThreadName = " + Thread.currentThread().getName() + "time=" + System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println("methodB end ThreadName = " + Thread.currentThread().getName() + "time=" + System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
package com.test;

public class Run {
    
    public static void main(String[] args) {
        
        MyService myService = new MyService();
        Thread t1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                myService.methodA();
            }
        });
        Thread t2 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                myService.methodB();
            }
        });
        t1.start();
        t2.start();
    }
}
結果:
methodA begin ThreadName = Thread-0time=1527821296840
methodA end ThreadName = Thread-0time=1527821299841
methodB begin ThreadName = Thread-1time=1527821299841
methodB end ThreadName = Thread-1time=1527821302841

注意:必需要在finally塊裏調用lock.unlock() 釋放鎖.spa

二、使用Condition實現等待/通知:線程

  awati() 與 signal() 方法:code

  

package com.test;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
    
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    
    public void testWait() {
        try {
            lock.lock();
            System.out.println("wait");
            condition.await();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void testSignal() {
        try {
            lock.lock();
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
    
}
  • 經過Condition對象來使線程wait,必須先執行lock.lock()得到鎖。
  • Condition對象的signal()方法能夠喚醒線程.
  • Condition的awati()方法和Object 中的wati()方法等效.
  • Condition的signal()方法和Object 中的notify()方法等效.
  • Condition的signalAll()方法和Object中的notifyAll()方法等效。

三、公平鎖和非公平鎖:對象

  公平鎖標識線程獲取鎖的順序是按照線程加鎖的順序來分配的。即先來先得的FIFO先進先出順序。而非公平鎖就是一種獲取鎖的搶佔機制,是隨機得到鎖的。blog

  

Lock lock=new ReentrantLock(true);//公平鎖
Lock lock=new ReentrantLock(false);//非公平鎖

四、ReentrantLock 類的方法:get

  • int getHoldCount() 的做用是查詢當前線程保持此鎖定的個數,也就是調用lock()方法的次數。
  • int getQueueLength()的所用是返回正在等待獲取此鎖的線程估計數 。好比有5個線程,1個線程首先執行await()方法,那麼調用此方法的返回值是4.
  • int getWaitQueueLength(Condition condition)的所用是返回等待與此鎖定相關的給定條件Condition的線程估計數,好比5個線程都執行了Condition的await()方法 ,那麼返回值就是5.
  • boolean hasQueuedThreads()的做用是查詢是否有線程正在等待獲取此鎖.
  • boolean hasWaiters(Condition condition)的做用是查詢是否有線程正在等待與此鎖定有關的condition條件。
  • boolean isFair()的做用是判斷是否是公平鎖.
  • boolean isHeldByCurrentThread()的做用是查詢當前線程是否保持此鎖。
  • boolean isLocked()的做用是查詢此鎖是否由任意線程保持。
  • void lockInterruptibly()的做用是:若是當前線程未被中斷,則獲取鎖定,若是已經被中斷則出現異常。
  • boolean tryLock()的做用是僅在調用時鎖定未被另外一個線程保持的狀況下,才獲取該鎖定。
  • boolean tryLock(long timeout, TimeUnit unit) 的做用是,若是鎖定在給定等待時間內沒有被領一個線程保持,且當前線程未被中斷,則獲取該鎖定。

五、ReentrantReadWriteLock 類,讀寫鎖:同步

  類ReentrantLock 具備徹底互斥排他的效果,即同一時間只有一個線程在執行lock()方法後面的任務。it

  讀寫鎖表示也有兩個鎖,一個是讀操做相關的鎖,也成爲共享鎖;另外一個是寫操做相關的鎖,也就排它鎖。也就是多個讀鎖之間不互斥,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥。在沒有線程進行寫操做時,進行讀取操做的多個線程均可以獲取讀鎖,而進行寫入操做的線程只有在獲取寫鎖後才能進行寫入操做。即多個線程能夠同時進行讀操做,但同一時刻只有一個線程能夠進行寫操做。

  • 讀讀共享

  

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read() {
        try {
            try {
                lock.readLock().lock();
                System.out.println("獲取讀鎖:" + System.currentTimeMillis());
                Thread.sleep(1000);
            } finally {
                lock.readLock().unlock();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  • 寫寫互斥

  

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void write() {
        try {
            try {
                lock.writeLock().lock();
                System.out.println("獲取寫鎖:" + System.currentTimeMillis());
                Thread.sleep(1000);
            } finally {
                lock.writeLock().unlock();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  • 讀寫互斥

  

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read() {
        try {
            try {
                lock.writeLock().lock();
                System.out.println("獲取讀鎖:" + System.currentTimeMillis());
                Thread.sleep(1000);
            } finally {
                lock.writeLock().unlock();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void write() {
        try {
            try {
                lock.writeLock().lock();
                System.out.println("獲取寫鎖:" + System.currentTimeMillis());
                Thread.sleep(1000);
            } finally {
                lock.writeLock().unlock();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 lock 與 lockInterruptibly比較區別在於:
 lock 優先考慮獲取鎖,待獲取鎖成功後,才響應中斷。
 lockInterruptibly 優先考慮響應中斷,而不是響應鎖的普通獲取或重入獲取

synchronized 和 lock 的用法區別:

  1. synchronized 是託管給JVM執行的。而Lock是Java寫的控制鎖的代碼。
  2. synchronized 原始採用的是CPU悲觀鎖機制,即線程得到的是獨佔鎖。獨佔鎖意味着其餘線程只能依靠阻塞來等待線程釋放鎖。而在CPU轉換線程阻塞時會引發線程上下文切換,會致使效率很低。
  3. Lock用的是樂觀鎖方式。每次不加鎖而是假設沒有衝突而去完成某項操做,若是由於衝突失敗就重試。直到成功爲止。
  4. ReentrantLock必須在finally中釋放鎖,而synchronized不須要。
  5. ReentrantLock提供了可輪詢的鎖請求,他能夠嘗試的去取得鎖,若是取得成功則繼續處理,取得不成功,能夠等下次運行的時候處理,因此不容易產生死鎖。而synchronized則一旦進入鎖請求要麼成功,要麼一直阻塞,因此更容易產生死鎖。
  6. synchronized的話,鎖的範圍是整個方法或synchronized塊部分;而Lock由於是方法調用,能夠跨方法,靈活性更大。

使用ReentrantLock的場景:

  1. 某個線程在等待一個鎖的控制權的這段時間須要中斷。
  2. 須要分開處理一些wait-notify,ReentrantLock裏面的Condition應用,可以控制notify哪一個線程.
  3. 具備公平鎖功能,每一個到來的線程都將排隊等候.
相關文章
相關標籤/搜索