天天看點

Java多線程程式設計-ReentrantLock(1)ReentrantLock簡單使用

Java多線程程式設計-ReentrantLock使用及其源碼解析

  • ReentrantLock簡單使用
    • ReentrantLock實作等待通知

在java多線程開發中,我們可以使用 synchronized關鍵字來實作線程之間的同步互斥,我們也可以使用 ReentrantLock實作更多的功能,如嗅探鎖定。多路分支通知等功能,使用上會比synchronized更加靈活。

ReentrantLock簡單使用

可重入的互斥Lock,具有與使用sync方法和語句通路的隐式螢幕鎖相同的基本行為和語義。

由上一次成功鎖定但尚未解鎖的線程所有。當該鎖不屬于另一個線程時,調用lock的線程将傳回,并成功擷取該鎖。如果目前線程已經擁有該鎖,則該方法将立即傳回。可以使用方法 isHeldByCurrentThread和getHoldCount進行檢查。

public class ReentrantLockV1Main {
    public static ReentrantLock reentrantLock = new ReentrantLock();
    public static void main(String[] args) {
        Thread thread01 = new Thread(() -> {
            reentrantLock.lock();
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + (i + 1));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }, "thread01");

        Thread thread02 = new Thread(() -> {
            reentrantLock.lock();
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + (i + 1));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }, "thread02");

        Thread thread03 = new Thread(() -> {
            reentrantLock.lock();
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + (i + 1));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }, "thread03");
        thread01.start();
        thread02.start();
        thread03.start();
    }
}
           

運作結果:

Java多線程程式設計-ReentrantLock(1)ReentrantLock簡單使用

使用lock方法擷取鎖,使用unlock方法釋放鎖。每個線程同步執行。

ReentrantLock實作等待通知

我們知道synchronized和wait/notify可以實作等待通知模式,ReentrantLock也可以實作,但是要借助Condition對象。在一個lock對象裡面可以建立多個condition執行個體,線程對象可以注冊在指定的condition中,進而可以有選擇性的進行線程的通知。在排程線程上更加靈活。

在沒有獲得鎖的情況下,進行wait操作會報錯:

public class ReentranLockObj1 {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void waitFun() {
        try {
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
           

線程類:

public class ReentranLockObj1T1 extends Thread {

    private ReentranLockObj1 reentranLockObj1;

    public ReentranLockObj1T1(ReentranLockObj1 reentranLockObj1) {
        this.reentranLockObj1 = reentranLockObj1;
    }

    @Override
    public void run() {
        reentranLockObj1.waitFun();
    }
}
           

運作結果:

Java多線程程式設計-ReentrantLock(1)ReentrantLock簡單使用

我們看看該類的Condition接口源碼:

public interface Condition {
//使目前線程等待,直到發出信号或被中斷
void await() throws InterruptedException;
//使目前線程等待,直到發出信号
void awaitUninterruptibly();
//使目前線程等待,直到發出信号或被中斷或經過指定的等待時間為止
long awaitNanos(long nanosTimeout) throws InterruptedException;
//使目前線程等待,直到發出信号或被中斷,或者經過指定的等待時間
boolean await(long time, TimeUnit unit) throws InterruptedException;
//使目前線程等待,直到發出信号或被中斷,或者經過指定的截止時間
boolean awaitUntil(Date deadline) throws InterruptedException;
//喚醒一個等待的線程
void signal();
//喚醒所有的等待線程
void signalAll();
}
           

我們隻需要将ReentranLockObj1 waitFun方法中添加lock即可,修改如下:

public void waitFun() {
        lock.lock();
        try {
            System.out.println("current thread name :" + Thread.currentThread().getName());
            condition.await();
            System.out.println("current thread name :" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
           

看看運作結果:

Java多線程程式設計-ReentrantLock(1)ReentrantLock簡單使用

await方法後面的語句沒有輸入到控制台,處于等待狀态。

進行通知喚醒:

public class ReentranLockObj2 {

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    /**
     * 等待
     */
    public void waitFun() {
        lock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName());
            condition.await();
            System.out.println("current thread name:" + Thread.currentThread().getName());
        } catch (IllegalMonitorStateException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 喚醒
     */
    public void notifyFun() {
        lock.lock();
        try {
            condition.signal();
        } catch (IllegalMonitorStateException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
           
public class ReentranLockObj1T2Main {

    public static void main(String[] args) throws InterruptedException {
        ReentranLockObj2 reentranLockObj2 = new ReentranLockObj2();
        ReentranLockObj2T2 reentranLockObj2T2 = new ReentranLockObj2T2(reentranLockObj2);
        reentranLockObj2T2.start();
        Thread.sleep(1000);
        reentranLockObj2.notifyFun();
    }
}
           

運作結果:

Java多線程程式設計-ReentrantLock(1)ReentrantLock簡單使用

列印第一句,1秒之後列印第二句,使用signal()喚醒。喚醒所有等待中的線程,使用signalAll()

public class ReentranLockObj3 {

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void waitFun1() {
        lock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName());
            condition.await();
            System.out.println("current thread name:" + Thread.currentThread().getName());
        } catch (IllegalMonitorStateException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void waitFun2() {
        lock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName());
            condition.await();
            System.out.println("current thread name:" + Thread.currentThread().getName());
        } catch (IllegalMonitorStateException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void notifyAllFun() {
        lock.lock();
        try {
            condition.signalAll();
        } catch (IllegalMonitorStateException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}
           

線程1:

public class ReentranLockObj3T1 extends Thread {

    private ReentranLockObj3 reentranLockObj3;

    public ReentranLockObj3T1(ReentranLockObj3 reentranLockObj3, String threadName) {
        this.reentranLockObj3 = reentranLockObj3;
        this.setName(threadName);
    }

    @Override
    public void run() {
        reentranLockObj3.waitFun1();
    }
}
           

線程2:

public class ReentranLockObj3T2 extends Thread {

    private ReentranLockObj3 reentranLockObj3;

    public ReentranLockObj3T2(ReentranLockObj3 reentranLockObj3, String threadName) {
        this.reentranLockObj3 = reentranLockObj3;
        this.setName(threadName);
    }

    @Override
    public void run() {
        reentranLockObj3.waitFun2();
    }
}
           

運作結果:

Java多線程程式設計-ReentrantLock(1)ReentrantLock簡單使用

先輸出上面兩行,等5分鐘通過調用signalAll進行喚醒所有等待中的線程。

如果想單獨喚醒一個,該怎麼處理呢? 我們需要建立多個condition進行處理了。通過condition喚醒部分線程,可以有效提高程式運作效率。

public class ReentranLockObj4 {

    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();

    public void waitFunCondition1() {
        lock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName());
            condition1.await();
            System.out.println("current thread name:" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void waitFunCondition2() {
        lock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName());
            condition2.await();
            System.out.println("current thread name:" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void notifyFunCondition1() {
        lock.lock();
        try {
            condition1.signalAll();
        } catch (IllegalMonitorStateException e) {
            e.printStackTrace();
        } finally {

            lock.unlock();
        }
    }

    public void notifyFunCondition2() {
        lock.lock();
        try {
            condition2.signalAll();
        } catch (IllegalMonitorStateException e) {
            e.printStackTrace();
        } finally {

            lock.unlock();
        }
    }

}
           

線程1:

public class ReentranLockObj4T1 extends Thread {

    private ReentranLockObj4 reentranLockObj4;

    public ReentranLockObj4T1(ReentranLockObj4 reentranLockObj4, String threadName) {
        this.reentranLockObj4 = reentranLockObj4;
        this.setName(threadName);
    }

    @Override
    public void run() {
        reentranLockObj4.waitFunCondition1();
    }
}
           

線程2:

public class ReentranLockObj4T2 extends Thread {

    private ReentranLockObj4 reentranLockObj4;

    public ReentranLockObj4T2(ReentranLockObj4 reentranLockObj4, String threadName) {
        this.reentranLockObj4 = reentranLockObj4;
        this.setName(threadName);
    }

    @Override
    public void run() {
        reentranLockObj4.waitFunCondition2();
    }
}
           

運作:

public class ReentranLockObj4Main {

    public static void main(String[] args) throws InterruptedException {
        ReentranLockObj4 reentranLockObj4 = new ReentranLockObj4();
        ReentranLockObj4T1 reentranLockObj4T1 = new ReentranLockObj4T1(reentranLockObj4, "thread01");
        reentranLockObj4T1.start();
        ReentranLockObj4T2 reentranLockObj4T2 = new ReentranLockObj4T2(reentranLockObj4, "thread02");
        reentranLockObj4T2.start();
        Thread.sleep(5000);
        reentranLockObj4.notifyFunCondition1();
    }

}
           

運作結果:

Java多線程程式設計-ReentrantLock(1)ReentrantLock簡單使用

有運作可以看出,當線程1喚醒的時候,調用線程1的wait後的方法,線程2沒有調用,就還在等待中,程式沒有結束。由此可以得出結論,ReentrantLock可以喚醒指定種類的線程。

總結一下:

1.Object類中的wait相當于Condition中的await

2.Object類中的wait(long timeout) 相當于Condition中的await(long timeout,TimeUnit unit)

3.Object類中的notify相當于Condition中的signal

4.Object類中的notifyAll相當于Condition中的signalAll

繼續閱讀