多線程Lock鎖


  在JDK1.5以后,在並發包(java.util.concurrent)里面添加包locks,並提供了Lock接口,用於與synchronized類似的鎖功能,不同的是Lock需要手動開啟鎖和釋放鎖。

 

為什么要用Lock鎖?

  1. 嘗試非阻塞的獲取鎖
  2. 獲取鎖的過程可以被中斷
  3. 超時獲取鎖

 

Lock鎖的實現類圖

 

Lock鎖的常用API

lock():加鎖

lockInterruptibly():可中斷

tryLock():嘗試非阻塞的獲取鎖

unlock():釋放鎖

public class AttemptLocking {
    private ReentrantLock lock = new ReentrantLock();
    public void untimed() {
        boolean captured = lock.tryLock();
        try {
            System.out.println("tryLock(): " + captured);
        } finally {
            if(captured)
                lock.unlock();
        }
    }
    public void timed() {
        boolean captured = false;
        try {
            captured = lock.tryLock(2, TimeUnit.SECONDS);
        } catch(InterruptedException e) {
            throw new RuntimeException(e);
        }
        try {
            System.out.println("tryLock(2, TimeUnit.SECONDS): " +
                captured);
        } finally {
            if(captured)
                lock.unlock();
        }
    }
    public static void main(String[] args) {
        final AttemptLocking al = new AttemptLocking();
        al.untimed();
        al.timed();
        new Thread() {
            { setDaemon(true); }
            public void run() {
                al.lock.lock();
                System.out.println("acquired");
            }
        }.start();
        Thread.yield();
        al.untimed();
        al.timed();
    }
}

 

 在untimed方法里如果tryLock()無法拿到鎖則離開去做別的事情;在timed方法里設置了等待時間為2秒,如果等待2秒還沒拿到鎖則離開去做別的事情。

 

Lock鎖使用模板

Lock lock = new ReentrantLock();
lock.lock();
try {
  // do my work
} finally {
  lock.unlock();
}

注:這里lock()方法不能寫到try里,因為如果自定義的lock發生異常,會導致空放解鎖。return要放到try里面,以保證unlock()不會過早發生,從而將數據暴露給了第二個任務。

 

Lock鎖的可重入性

  可重入性是指當前線程可以重復進入被自己鎖的代碼塊。如果沒有可重入性,當遇到遞歸操作時就會發生死鎖現象。

 

公平鎖和非公平鎖

  • 公平鎖:在多個線程等待鎖的時候,當鎖被釋放時,保證最先請求的線程先得到鎖(也就是等待時間最長的線程),保證了每個線程都可以得到鎖。
  • 非公平鎖:與公平鎖不一樣的是,非公平鎖不敢保證每個線程都可以拿到鎖,但是可以提高線程交替的效率,從而獲得高執行率。

 

讀寫鎖ReentrantReadWriteLock

  ReentrantReadWriteLock可以在多個線程同時進行時,允許一個寫線程(不允許其他讀線程和寫線程)或者多個讀線程的操作,支持讀多寫少的業務場景,提高程序性能。

讀寫鎖模板

public class RwLockTemplate {

    private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private Lock read = reentrantReadWriteLock.readLock();
    private Lock write = reentrantReadWriteLock.writeLock();
    
    public void set(Object params) {
        this.write.lock();
        try {
            // do my work
        } finally {
            this.write.unlock();
        }
    }
    
    public Object get() {
        this.read.lock();
        try {
            // do my work
            return null;
        } finally {
            this.read.unlock();
        }
    }
}

 

Condition接口

  既然已經有了Lock鎖,那么他也會有一套等待通知機制,他就是Condition接口,對應synchronized的wait()、notify()、notifyAll()方法。

condition模板

public class ConditionTemplete {

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    
    public void waitc() throws InterruptedException {
        lock.lock();
        try {
            condition.await();
        } finally {
            lock.unlock();
        }
    }
    
    public void notityc() throws InterruptedException {
        lock.lock();
        try {
            condition.signal();
            //condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

這里await()方法就是等待,signal()/signalAll()方法就是通知,condition可以有多個

注:這里的signal()/signalAll()方法和synchronized的notify()/notifyAll()方法有所不同,signal()通知這個lock的所有等待signalAll()通知程序中所有lock的所有等待

 

使用Lock和Condition實現阻塞隊列

public class BlockingQueueLC<T> {

    private List<T> queue = new LinkedList<>();
    private int limit;
    private Lock lock = new ReentrantLock();
    private Condition needNotEmpty = lock.newCondition();
    private Condition needNotFull = lock.newCondition();
    
    public BlockingQueueLC(int limit) {
        this.limit = limit;
    }

    public void push(T el) throws InterruptedException {
        this.lock.lock();
        try {
            while (this.limit == this.queue.size()) {
                this.needNotFull.await();
            }
            this.queue.add(el);
            this.needNotEmpty.signal();
        } finally {
            this.lock.unlock();
        }
    }
    
    public T pull() throws InterruptedException {
        this.lock.lock();
        try {
            while (0 == this.queue.size()) {
                this.needNotEmpty.await();
            }
            this.needNotFull.signal();
            return this.queue.get(0);
        } finally {
            lock.unlock();
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM