自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聊一聊比Synchronized更強(qiáng)大的同步鎖:ReentrantLock

開發(fā) 前端
從 JDK 1.5 開始,引入了一個(gè)高級(jí)的處理并發(fā)的java.util.concurrent包,它提供了大量更高級(jí)的并發(fā)功能,能大大的簡化多線程程序的編寫。

01、背景介紹

我們介紹到了使用synchronized關(guān)鍵字可以實(shí)現(xiàn)線程同步安全的效果,以及采用wait()、notify()和notifyAll()方法,可以實(shí)現(xiàn)多個(gè)線程之間的通信協(xié)調(diào),基本可以滿足并發(fā)編程的需求。

但是采用synchronized進(jìn)行加鎖,這種鎖一般都比較重,里面的實(shí)現(xiàn)機(jī)制也非常復(fù)雜,同時(shí)獲取鎖時(shí)必須一直等待,沒有額外的嘗試機(jī)制,如果編程不當(dāng),可能就容易發(fā)生死鎖現(xiàn)象。

從 JDK 1.5 開始,引入了一個(gè)高級(jí)的處理并發(fā)的java.util.concurrent包,它提供了大量更高級(jí)的并發(fā)功能,能大大的簡化多線程程序的編寫。

比如我們今天要介紹的java.util.concurrent.locks包提供的ReentrantLock類,一個(gè)可重入的互斥鎖,它具有與使用synchronized加鎖一樣的特性,并且功能更加強(qiáng)大。

下面我們一起來學(xué)習(xí)一下ReentrantLock類的基本玩法。

02、ReentrantLock 玩法介紹

在介紹ReentrantLock之前,我們先來看一下傳統(tǒng)的使用synchronized對(duì)方法進(jìn)行加鎖的示例。

public class Counter {

    private int count;

    public void add() {
        synchronized(this) {
            count ++;
            System.out.println("ThreadName:" + Thread.currentThread().getName() + ", count:" + getCount());
        }
    }

    public int getCount() {
        return count;
    }
}
public static void main(String[] args) throws InterruptedException {
    Counter counter = new Counter();

    // 創(chuàng)建5個(gè)線程,同時(shí)對(duì)count進(jìn)行加一操作
    for (int i = 0; i < 5; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                counter.add();
            }
        }).start();
    }

    // 假設(shè)休眠1秒,5個(gè)線程執(zhí)行完畢
    Thread.sleep(1000);
    System.out.println("count:" + counter.getCount());
}

輸出結(jié)果如下:

ThreadName:Thread-0, count:1
ThreadName:Thread-1, count:2
ThreadName:Thread-2, count:3
ThreadName:Thread-3, count:4
ThreadName:Thread-4, count:5
count:5

如果用ReentrantLock替代,只需要將Counter中的代碼改造為如下:

public class Counter {

    private final Lock lock = new ReentrantLock();

    private int count;

    public void add() {
        // 加鎖
        lock.lock();
        try {
            count ++;
            System.out.println("ThreadName:" + Thread.currentThread().getName() + ", count:" + getCount());
        } finally {
            // 釋放鎖
            lock.unlock();
        }
    }
    
    public int getCount() {
        return count;
    }
}

運(yùn)行程序,結(jié)果與上面一致,可以證明:ReentrantLock具備與synchronized一樣的加鎖功能。

同時(shí),ReentrantLock還具備在指定的時(shí)間內(nèi)嘗試獲取鎖的機(jī)制,比如下面這行代碼:

if (lock.tryLock(3, TimeUnit.SECONDS)) {
    try {
        ...
    } finally {
        lock.unlock();
    }
}

嘗試在 3 秒內(nèi)獲取鎖,如果獲取不到就返回false,程序不需要無限等待下去,這個(gè)功能在實(shí)際開發(fā)中使用非常的廣泛。

從上面的示例代碼,我們可以總結(jié)出synchronized和ReentrantLock有以下幾點(diǎn)不一樣。

  • ReentrantLock需要手動(dòng)調(diào)用加鎖方法;而synchronized不需要,它采用了隱藏的加鎖方式,借助 jvm 來實(shí)現(xiàn)
  • synchronized不需要考慮異常;而ReentrantLock獲取鎖之后,要在finally中正確的釋放鎖,否則會(huì)影響其它線程
  • ReentrantLock擁有嘗試獲取鎖的超時(shí)機(jī)制,利用它可以避免無限等待;而synchronized不具備
  • synchronized是 Java 語言層面提供的語法;而ReentrantLock是 Java 代碼實(shí)現(xiàn)的可重入鎖

因此,在并發(fā)編程中,使用ReentrantLock比直接使用synchronized更靈活、更安全,采用tryLock(long time, TimeUnit unit)方法,即使未獲取到鎖也不會(huì)導(dǎo)致死鎖。

03、ReentrantLock 和 synchronized 持有的對(duì)象監(jiān)視器是同一個(gè)嗎?

可能有的同學(xué)會(huì)發(fā)出這樣的一個(gè)問題,使用ReentrantLock進(jìn)行加鎖和使用synchronized加鎖,兩者持有的對(duì)象監(jiān)視器是同一個(gè)嗎?

下面我們一起來看一個(gè)例子。

public class Counter {

    private final Lock lock = new ReentrantLock();

    private int count;


    public synchronized void methodA() {
        System.out.println("ThreadName:" + Thread.currentThread().getName() + ",begin methodA, count:" + getCount());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count ++;
        System.out.println("ThreadName:" + Thread.currentThread().getName() + ", count:" + getCount());

    }

    public void methodB() {
        // 加鎖
        lock.lock();
        try {
            System.out.println("ThreadName:" + Thread.currentThread().getName() + ",begin methodB, count:" + getCount());
            Thread.sleep(3000);
            count ++;
            System.out.println("ThreadName:" + Thread.currentThread().getName() + ", count:" + getCount());
        } catch (Exception e){
          e.printStackTrace();
        } finally {
            // 釋放鎖
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}
public class MyThreadA extends Thread {

    private Counter counter;

    public MyThreadA(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        counter.methodA();
    }
}
public class MyThreadB extends Thread {

    private Counter counter;

    public MyThreadB(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        counter.methodB();
    }
}
public class MyThreadTest {

    public static void main(String[] args) {
        Counter counter = new Counter();

        MyThreadA threadA = new MyThreadA(counter);
        threadA.start();

        MyThreadB threadB = new MyThreadB(counter);
        threadB.start();
    }
}

看一下運(yùn)行結(jié)果:

ThreadName:Thread-0,begin methodA, count:0
ThreadName:Thread-1,begin methodB, count:0
ThreadName:Thread-0, count:2
ThreadName:Thread-1, count:2

從日志上可以看出,采用兩個(gè)線程分別采用synchronized和ReentrantLock兩種加鎖方式對(duì)count進(jìn)行操作,兩個(gè)線程交替執(zhí)行,可以得出一個(gè)結(jié)論:synchronized和ReentrantLock持有的對(duì)象監(jiān)視器不同。

04、Condition 基本用法

在之前的文章中,我們介紹了在synchronized同步方法/代碼塊中,使用wait()、notify()和notifyAll()可以實(shí)現(xiàn)線程之間的等待/通知模型。

ReentrantLock同樣也可以,只需要借助Condition類即可實(shí)現(xiàn),Condition提供的await()、signal()、signalAll()原理和synchronized鎖對(duì)象的wait()、notify()、notifyAll()是一致的,并且其行為也是一樣的。

我們還是先來看一個(gè)簡單的示例。

public class Counter {

    private final Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    private int count;

    public void await(){
        // 加鎖
        lock.lock();
        try {
            condition.await();
            System.out.println("await等待結(jié)束,count:" + getCount());
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            // 釋放鎖
            lock.unlock();
        }
    }


    public void signal(){
        // 加鎖
        lock.lock();
        try {
            count++;
            // 喚醒某個(gè)等待線程
            condition.signal();
            // 喚醒所有等待線程
//            condition.signalAll();
            System.out.println("signal 喚醒通知完畢");
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            // 釋放鎖
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}
public class MyThreadA extends Thread {

    private Counter counter;

    public MyThreadA(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        counter.await();
    }
}
public class MyThreadB extends Thread {

    private Counter counter;

    public MyThreadB(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        counter.signal();
    }
}
public class MyThreadTest {

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        // 先啟動(dòng)執(zhí)行等待的線程
        MyThreadA threadA = new MyThreadA(counter);
        threadA.start();

        Thread.sleep(3000);

        // 過3秒,再啟動(dòng)執(zhí)行通知的線程
        MyThreadB threadB = new MyThreadB(counter);
        threadB.start();
    }
}

看一下運(yùn)行結(jié)果:

signal 通知完畢
await等待結(jié)束,count:1

從結(jié)果上看很明顯的看出,等待線程MyThreadA先啟動(dòng),過了 3 秒之后再啟動(dòng)了MyThreadB,但是signal()方法先執(zhí)行完畢,再通知await()方法執(zhí)行,符合代碼預(yù)期。

這個(gè)例子也證明了一點(diǎn):condition.await()方法是釋放了鎖,不然signal()方法體不會(huì)被執(zhí)行。

相比wait/notify/notifyAll的等待/通知模型,Condition更加靈活,理由有以下幾點(diǎn):

  • notify()方法喚醒線程時(shí),被通知的線程由 Java 虛擬機(jī)隨機(jī)選擇;而采用ReentrantLock結(jié)合Condition可以實(shí)現(xiàn)有選擇性地通知,這一特性在實(shí)際編程中非常實(shí)用
  • 一個(gè)Lock里面可以創(chuàng)建多個(gè)Condition實(shí)例,實(shí)現(xiàn)多路通知,使用多個(gè)Condition的應(yīng)用場景很常見,比如ArrayBlockingQueue

05、參考

1、https://www.cnblogs.com/xrq730/p/4855155.html

2、https://www.liaoxuefeng.com/wiki/

責(zé)任編輯:武曉燕 來源: 潘志的研發(fā)筆記
相關(guān)推薦

2022-02-21 15:01:45

MySQL共享鎖獨(dú)占鎖

2019-12-12 14:52:10

數(shù)據(jù)庫腳本

2020-02-02 13:59:59

MySQL數(shù)據(jù)庫線程

2023-07-06 13:56:14

微軟Skype

2023-05-09 12:46:00

linuxlock

2023-10-07 08:17:40

公平鎖非公平鎖

2020-09-08 06:54:29

Java Gradle語言

2023-09-22 17:36:37

2020-05-22 08:16:07

PONGPONXG-PON

2021-01-28 22:31:33

分組密碼算法

2021-03-11 08:55:47

JavaUser對(duì)象

2021-08-01 09:55:57

Netty時(shí)間輪中間件

2023-09-27 16:39:38

2024-10-28 21:02:36

消息框應(yīng)用程序

2018-06-07 13:17:12

契約測試單元測試API測試

2021-12-06 09:43:01

鏈表節(jié)點(diǎn)函數(shù)

2021-03-01 18:37:15

MySQL存儲(chǔ)數(shù)據(jù)

2023-09-20 23:01:03

Twitter算法

2021-07-16 11:48:26

模型 .NET微軟

2022-08-08 08:25:21

Javajar 文件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)