并發(fā) -- Lock & Condition

一、概念

synchronized是Java語言的關(guān)鍵字,是內(nèi)置特性,而ReentrantLock是一個(gè)實(shí)現(xiàn)了Lock接口的類,通過該類可以實(shí)現(xiàn)線程的同步。

synchronized和ReentrantLock都是可重入鎖。當(dāng)一個(gè)線程執(zhí)行到某個(gè)synchronized方法時(shí),比如method1,而在method1中會(huì)調(diào)用另外一個(gè)synchronized方法,比如method2,此時(shí)線程不必重新去申請(qǐng)鎖,而是可以直接執(zhí)行方法method2。

二、ReentrantLock

主要方法:
lock():獲取不到鎖就不罷休,否則線程一直處于阻塞狀態(tài)。
tryLock():嘗試性地獲取鎖,不管有沒有獲取到都馬上返回,拿到鎖就返回true,不然就返回false。
tryLock(long time, TimeUnit unit):如果獲取不到鎖,就等待一段時(shí)間,超時(shí)返回false。
lockInterruptibly():線程調(diào)用lockInterruptibly()方法獲取鎖,未獲取到處于阻塞狀態(tài),此時(shí)如果調(diào)用該線程的interrupt()方法會(huì)喚醒該線程并要求處理InterruptedException。

static class MyThread extends Thread {
    private static Lock lock = new ReentrantLock();

    private MyThread(String name) {
        setName(name);
    }

    @Override
    public void run() {
        Log.d(TAG, "zwm, thread: " + getName() + " run start");
        try {
            lock.lockInterruptibly();
            Log.d(TAG, "zwm, thread: " + getName() + " acquire lock");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            Log.d(TAG, "zwm, thread: " + getName() + " InterruptedException");
        } finally {
            try {
                lock.unlock();
                Log.d(TAG, "zwm, thread: " + getName() + " release lock");
            } catch (IllegalMonitorStateException e) {
                e.printStackTrace();
                Log.d(TAG, "zwm, thread: " + getName() + " IllegalMonitorStateException");
            }
        }
        Log.d(TAG, "zwm, thread: " + getName() + " run end");
    }
}

//測(cè)試代碼
MyThread thread1 = new MyThread("thread1");
thread1.start();
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

MyThread thread2 = new MyThread("thread2");
thread2.start();
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

thread2.interrupt();

//輸出
02-11 21:22:13.046 zwm, thread: thread1 run start
02-11 21:22:13.046 zwm, thread: thread1 acquire lock
02-11 21:22:14.046 zwm, thread: thread2 run start
02-11 21:22:15.048 zwm, thread: thread2 InterruptedException
02-11 21:22:15.049 zwm, thread: thread2 IllegalMonitorStateException
02-11 21:22:15.049 zwm, thread: thread2 run end
02-11 21:22:23.047 zwm, thread: thread1 release lock
02-11 21:22:23.047 zwm, thread: thread1 run end

三、ReentrantReadWriteLock

可重入讀寫鎖,實(shí)現(xiàn)了ReadWriteLock接口:

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}

現(xiàn)實(shí)中有這樣一種場(chǎng)景:對(duì)共享資源有讀和寫的操作,且寫操作沒有讀操作那么頻繁。在沒有寫操作的時(shí)候,多個(gè)線程同時(shí)讀一個(gè)資源沒有任何問題,所以應(yīng)該允許多個(gè)線程同時(shí)讀取共享資源;但是如果一個(gè)線程想去寫這些共享資源,就不應(yīng)該允許其他線程對(duì)該資源進(jìn)行讀和寫的操作了。

針對(duì)這種場(chǎng)景,Java的并發(fā)包提供了讀寫鎖ReentrantReadWriteLock,它表示兩個(gè)鎖,一個(gè)是讀操作相關(guān)的鎖,稱為共享鎖;一個(gè)是寫相關(guān)的鎖,稱為排他鎖。

線程進(jìn)入讀鎖的前提條件:

  • 沒有其他線程的寫鎖。
  • 沒有寫請(qǐng)求,或者有寫請(qǐng)求但調(diào)用線程和持有鎖的線程是同一個(gè)。

線程進(jìn)入寫鎖的前提條件:

  • 沒有其他線程的讀鎖。
  • 沒有其他線程的寫鎖。

讀寫鎖有以下三個(gè)重要的特性:

  • 公平選擇性:支持非公平(默認(rèn))和公平的鎖獲取方式,吞吐量還是非公平優(yōu)于公平。
  • 重進(jìn)入:讀鎖和寫鎖都支持線程重進(jìn)入。
  • 鎖降級(jí):遵循獲取寫鎖、獲取讀鎖、再釋放寫鎖的次序,寫鎖能夠降級(jí)成為讀鎖。

讀鎖:

static class MyThread extends Thread{
    private static ReentrantReadWriteLock.ReadLock lock = new ReentrantReadWriteLock().readLock();

    private MyThread(String name) {
        setName(name);
    }

    @Override
    public void run() {
        Log.d(TAG, "zwm, thread: " + getName() + " run start");
        lock.lock();
        Log.d(TAG, "zwm, thread: " + getName() + " acquire lock");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            Log.d(TAG, "zwm, thread: " + getName() + " release lock");
        }
        Log.d(TAG, "zwm, thread: " + getName() + " run end");
    }
}

//測(cè)試代碼 
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.start();
MyThread thread3 = new MyThread("thread3");
thread3.start();

//輸出
02-11 22:52:10.112 zwm, thread: thread1 run start
02-11 22:52:10.112 zwm, thread: thread1 acquire lock
02-11 22:52:10.113 zwm, thread: thread2 run start
02-11 22:52:10.113 zwm, thread: thread3 run start
02-11 22:52:10.113 zwm, thread: thread2 acquire lock
02-11 22:52:10.113 zwm, thread: thread3 acquire lock
02-11 22:52:12.113 zwm, thread: thread1 release lock
02-11 22:52:12.113 zwm, thread: thread1 run end
02-11 22:52:12.114 zwm, thread: thread3 release lock
02-11 22:52:12.114 zwm, thread: thread3 run end
02-11 22:52:12.114 zwm, thread: thread2 release lock
02-11 22:52:12.114 zwm, thread: thread2 run end

寫鎖:

static class MyThread extends Thread{
    private static ReentrantReadWriteLock.WriteLock lock = new ReentrantReadWriteLock().writeLock();

    private MyThread(String name) {
        setName(name);
    }

    @Override
    public void run() {
        Log.d(TAG, "zwm, thread: " + getName() + " run start");
        lock.lock();
        Log.d(TAG, "zwm, thread: " + getName() + " acquire lock");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            Log.d(TAG, "zwm, thread: " + getName() + " release lock");
        }
        Log.d(TAG, "zwm, thread: " + getName() + " run end");
    }
}

//測(cè)試代碼
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.start();
MyThread thread3 = new MyThread("thread3");
thread3.start();

//輸出
02-11 22:55:44.916 zwm, thread: thread3 run start
02-11 22:55:44.917 zwm, thread: thread3 acquire lock
02-11 22:55:44.917 zwm, thread: thread2 run start
02-11 22:55:44.917 zwm, thread: thread1 run start
02-11 22:55:46.917 zwm, thread: thread2 acquire lock
02-11 22:55:46.917 zwm, thread: thread3 release lock
02-11 22:55:46.918 zwm, thread: thread3 run end
02-11 22:55:48.918 zwm, thread: thread1 acquire lock
02-11 22:55:48.918 zwm, thread: thread2 release lock
02-11 22:55:48.919 zwm, thread: thread2 run end
02-11 22:55:50.919 zwm, thread: thread1 release lock
02-11 22:55:50.919 zwm, thread: thread1 run end

四、Lock與synchronized的比較

  • Lock是一個(gè)接口,而synchronized是Java中的關(guān)鍵字,synchronized是內(nèi)置的語言實(shí)現(xiàn)。
  • synchronized在發(fā)生異常時(shí),會(huì)自動(dòng)釋放線程占有的鎖,因此不會(huì)導(dǎo)致死鎖現(xiàn)象發(fā)生。Lock在發(fā)生異常時(shí),如果沒有主動(dòng)通過unLock()方法去釋放鎖,則很可能造成死鎖的現(xiàn)象,因此使用Lock時(shí)需要在finally塊中釋放鎖。
  • Lock可以讓等待鎖的線程響應(yīng)中斷,而synchronized卻不行,使用synchronized時(shí),等待的線程會(huì)一直等待下去,不能夠響應(yīng)中斷。
  • 通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。
  • Lock可以提高多個(gè)線程進(jìn)行讀操作的效率。

五、Condition

Lock用于控制多線程對(duì)同一狀態(tài)的順序訪問,保證該狀態(tài)的連續(xù)性。
Condition用于控制多線程之間的基于該狀態(tài)的條件等待。

生產(chǎn)者消費(fèi)者簡(jiǎn)單模型:生產(chǎn)者往buffer里put,消費(fèi)者從buffer里take。
1.同一狀態(tài)的順序訪問
有三個(gè)狀態(tài)需要順序訪問:buffer的大小count,生產(chǎn)者用于put的游標(biāo)putptr,消費(fèi)者用于take的游標(biāo)takeptr。
2.基于該狀態(tài)的條件等待
當(dāng)count = 0時(shí),消費(fèi)者的take需要等待;當(dāng)count = buffer.size時(shí),生產(chǎn)者的put需要等待。

使用Lock & Condition實(shí)現(xiàn)生產(chǎn)者消費(fèi)者簡(jiǎn)單模型:

class BoundedBuffer {
    final Lock lock = new ReentrantLock();
    final Condition notFull = lock.newCondition();
    final Condition notEmpty = lock.newCondition();
    final Object[] items = new Object[5];
    int putptr, takeptr, count;

    public void put(Object x) throws InterruptedException {
        lock.lock();
        Log.d(TAG, "zwm, put method, thread: " + Thread.currentThread().getName() + " acquire lock");
        try {
            while (count == items.length) {
                Log.d(TAG, "zwm, buffer full, thread: " + Thread.currentThread().getName() + " await using notFull condition");
                notFull.await();
            }
            items[putptr] = x;
            Log.d(TAG, "zwm, thread: " + Thread.currentThread().getName() + " putptr: " + putptr + ", value: " + items[putptr]);
            if (++putptr == items.length) putptr = 0;
            ++count;
            Log.d(TAG, "zwm, put done, thread: " + Thread.currentThread().getName() + " signal other thread using notEmpty condition");
            notEmpty.signal();
        } finally {
            lock.unlock();
            Log.d(TAG, "zwm, put method, thread: " + Thread.currentThread().getName() + " release lock");
        }
    }

    public Object take() throws InterruptedException {
        lock.lock();
        Log.d(TAG, "zwm, take method, thread: " + Thread.currentThread().getName() + " acquire lock");
        try {
            while (count == 0) {
                Log.d(TAG, "zwm, buffer empty, thread: " + Thread.currentThread().getName() + " await using notEmpty condition");
                notEmpty.await();
            }
            Object x = items[takeptr];
            Log.d(TAG, "zwm, thread: " + Thread.currentThread().getName() + " takeptr: " + takeptr + ", value: " + items[takeptr]);
            if (++takeptr == items.length) takeptr = 0;
            --count;
            Log.d(TAG, "zwm, take done, thread: " + Thread.currentThread().getName() + " signal other thread using notFull condition");
            notFull.signal();
            return x;
        } finally {
            lock.unlock();
            Log.d(TAG, "zwm, take method, thread: " + Thread.currentThread().getName() + " release lock");
        }
    }
}

class PutThread extends Thread {
    private BoundedBuffer buffer;

    public PutThread(String name, BoundedBuffer buffer) {
        setName(name);
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for(int i=0; i<5; i++) {
            try {
                buffer.put(String.valueOf(i));
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class TakeThread extends Thread {
    private BoundedBuffer buffer;

    public TakeThread(String name, BoundedBuffer buffer) {
        setName(name);
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for(int i=0; i<5; i++) {
            try {
                buffer.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//測(cè)試代碼
BoundedBuffer buffer = new BoundedBuffer();
PutThread putThread1 = new PutThread("putThread1", buffer);
putThread1.start();
TakeThread takeThread1 = new TakeThread("takeThread1", buffer);
takeThread1.start();
TakeThread takeThread2 = new TakeThread("takeThread2", buffer);
takeThread2.start();

//輸出
2019-02-12 14:27:44.717 zwm, put method, thread: putThread1 acquire lock
2019-02-12 14:27:44.717 zwm, thread: putThread1 putptr: 0, value: 0
2019-02-12 14:27:44.717 zwm, put done, thread: putThread1 signal other thread using notEmpty condition
2019-02-12 14:27:44.717 zwm, put method, thread: putThread1 release lock
2019-02-12 14:27:44.718 zwm, take method, thread: takeThread1 acquire lock
2019-02-12 14:27:44.719 zwm, thread: takeThread1 takeptr: 0, value: 0
2019-02-12 14:27:44.719 zwm, take done, thread: takeThread1 signal other thread using notFull condition
2019-02-12 14:27:44.719 zwm, take method, thread: takeThread1 release lock
2019-02-12 14:27:44.719 zwm, take method, thread: takeThread2 acquire lock
2019-02-12 14:27:44.719 zwm, buffer empty, thread: takeThread2 await using notEmpty condition //buffer空,takeThread2等待,釋放鎖
2019-02-12 14:27:44.720 zwm, take method, thread: takeThread1 acquire lock //takeThread1獲取鎖
2019-02-12 14:27:44.721 zwm, buffer empty, thread: takeThread1 await using notEmpty condition //buffer空,takeThread1等待,釋放鎖
2019-02-12 14:27:45.718 zwm, put method, thread: putThread1 acquire lock
2019-02-12 14:27:45.718 zwm, thread: putThread1 putptr: 1, value: 1
2019-02-12 14:27:45.718 zwm, put done, thread: putThread1 signal other thread using notEmpty condition
2019-02-12 14:27:45.718 zwm, put method, thread: putThread1 release lock
2019-02-12 14:27:45.718 zwm, thread: takeThread2 takeptr: 1, value: 1 //takeThread2被喚醒,獲取鎖
2019-02-12 14:27:45.718 zwm, take done, thread: takeThread2 signal other thread using notFull condition
2019-02-12 14:27:45.719 zwm, take method, thread: takeThread2 release lock
2019-02-12 14:27:45.719 zwm, take method, thread: takeThread2 acquire lock //takeThread2獲取鎖
2019-02-12 14:27:45.719 zwm, buffer empty, thread: takeThread2 await using notEmpty condition //buffer空,takeThread2等待,釋放鎖
2019-02-12 14:27:46.719 zwm, put method, thread: putThread1 acquire lock
2019-02-12 14:27:46.719 zwm, thread: putThread1 putptr: 2, value: 2
2019-02-12 14:27:46.720 zwm, put done, thread: putThread1 signal other thread using notEmpty condition
2019-02-12 14:27:46.720 zwm, put method, thread: putThread1 release lock
2019-02-12 14:27:46.721 zwm, thread: takeThread1 takeptr: 2, value: 2 //takeThread1被喚醒,獲取鎖
2019-02-12 14:27:46.721 zwm, take done, thread: takeThread1 signal other thread using notFull condition
2019-02-12 14:27:46.721 zwm, take method, thread: takeThread1 release lock
2019-02-12 14:27:46.722 zwm, take method, thread: takeThread1 acquire lock //takeThread1獲取鎖
2019-02-12 14:27:46.722 zwm, buffer empty, thread: takeThread1 await using notEmpty condition //buffer空,takeThread1等待,釋放鎖
2019-02-12 14:27:47.721 zwm, put method, thread: putThread1 acquire lock
2019-02-12 14:27:47.721 zwm, thread: putThread1 putptr: 3, value: 3
2019-02-12 14:27:47.721 zwm, put done, thread: putThread1 signal other thread using notEmpty condition
2019-02-12 14:27:47.721 zwm, put method, thread: putThread1 release lock
2019-02-12 14:27:47.722 zwm, thread: takeThread2 takeptr: 3, value: 3
2019-02-12 14:27:47.722 zwm, take done, thread: takeThread2 signal other thread using notFull condition
2019-02-12 14:27:47.722 zwm, take method, thread: takeThread2 release lock
2019-02-12 14:27:47.722 zwm, take method, thread: takeThread2 acquire lock
2019-02-12 14:27:47.722 zwm, buffer empty, thread: takeThread2 await using notEmpty condition //buffer空,takeThread2等待,釋放鎖
2019-02-12 14:27:48.722 zwm, put method, thread: putThread1 acquire lock
2019-02-12 14:27:48.722 zwm, thread: putThread1 putptr: 4, value: 4
2019-02-12 14:27:48.722 zwm, put done, thread: putThread1 signal other thread using notEmpty condition
2019-02-12 14:27:48.723 zwm, put method, thread: putThread1 release lock
2019-02-12 14:27:48.723 zwm, thread: takeThread1 takeptr: 4, value: 4
2019-02-12 14:27:48.723 zwm, take done, thread: takeThread1 signal other thread using notFull condition
2019-02-12 14:27:48.724 zwm, take method, thread: takeThread1 release lock
2019-02-12 14:27:48.724 zwm, take method, thread: takeThread1 acquire lock
2019-02-12 14:27:48.724 zwm, buffer empty, thread: takeThread1 await using notEmpty condition //buffer空,takeThread1等待,釋放鎖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容