可重入鎖:ReentrantLock理解使用

https://blog.csdn.net/u014730165/article/details/82144848

(一)可重入性
可重入性描述這樣的一個問題:一個線程在持有一個鎖的時候,它內部能否再次(多次)申請該鎖。如果一個線程已經獲得了鎖,其內部還可以多次申請該鎖成功。那麼我們就稱該鎖爲可重入鎖。通過以下僞代碼說明:

void methodA(){
    lock.lock(); // 獲取鎖
    methodB();
    lock.unlock() // 釋放鎖
}

void methodB(){
    lock.lock(); // 獲取鎖
    // 其他業務
    lock.unlock();// 釋放鎖
}

可重入鎖可以理解爲鎖的一個標識。該標識具備計數器功能。標識的初始值爲0,表示當前鎖沒有被任何線程持有。每次線程獲得一個可重入鎖的時候,該鎖的計數器就被加1。每次一個線程釋放該所的時候,該鎖的計數器就減1。前提是:當前線程已經獲得了該鎖,是在線程的內部出現再次獲取鎖的場景

(二)Lock接口,ReentrantLock說明
2.1 Lock接口說明
Modifier and Type    Method    Description
void    lock()    獲取鎖
void    lockInterruptibly()    除非當前線程被中斷,否則獲取鎖定
Condition    newCondition()    返回綁定到此Lock實例的新Condition實例
boolean    tryLock()    只有在調用時它是空閒的才能獲取鎖
boolean    tryLock(long time, TimeUnit unit)    如果在給定的等待時間內空閒並且當前線程未被中斷,則獲取鎖
void    unlock()    釋放鎖
2.2 ReentrantLock實現說明
ReentrantLock是Lock接口的一個實現類。爲了演示Lock接口的方法,我們以ReentrantLock作爲示例說明。

2.2.1 lock(),unlock()方法說明
該demo模擬電影院的售票情況,tickets總票數。開啓了10個窗口售票,售完爲止

public class ReentrantLockDemo01 implements Runnable {

    private Lock lock = new ReentrantLock();

    private int tickets = 200;

    @Override
    public void run() {
        while (true) {
            lock.lock(); // 獲取鎖
            try {
                if (tickets > 0) {
                    TimeUnit.MILLISECONDS.sleep(100);
                    System.out.println(Thread.currentThread().getName() + " " + tickets--);
                } else {
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock(); // 釋放所
            }
        }
    }

    public static void main(String[] args) {
        ReentrantLockDemo01 reentrantLockDemo = new ReentrantLockDemo01();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(reentrantLockDemo, "thread" + i);
            thread.start();
        }
    }
}

2.2.2 lockInterruptibly()方法說明
從Lock的源碼可以看出:lockInterruptibly() 拋出中斷異常

void lockInterruptibly() throws InterruptedException;
1
在synchronize關鍵字中,同步代碼塊發送阻塞的情況,例如:wait(),sleep(),jion()等情況下,可以被中斷。中斷並不意味着線程已經終止

代碼示例如下:

public class ReentrantLockDemo02 implements Runnable {

    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        try {
            lock.lockInterruptibly();
            System.out.println(Thread.currentThread().getName() + " running");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + " finished");
            lock.unlock();
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + " interrupted");
        }
    }

    public static void main(String[] args) {
        ReentrantLockDemo02 reentrantLockDemo = new ReentrantLockDemo02();
        Thread thread01 = new Thread(reentrantLockDemo, "thread01");
        Thread thread02 = new Thread(reentrantLockDemo, "thread02");
        thread01.start();
        thread02.start();
        thread02.interrupt();
    }
}

輸出結果:

thread01 running
thread02 interrupted
thread01 finished
1
2
3
從輸出結果可以看出,thread01正常結束程序,thread02被中斷程序,執行catch中的代碼塊

2.2.3 tryLock(),tryLock(long time, TimeUnit unit)方法說明
tryLock()方法立刻返回當前獲取情況。

tryLock(long time, TimeUnit unit)等待一定的時間,返回獲取情況

public class ReentrantLockDemo03 implements Runnable {

    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        try {
            if (lock.tryLock(2, TimeUnit.SECONDS)) {
                System.out.println(Thread.currentThread().getName() + " 獲取當前lock鎖");
                TimeUnit.SECONDS.sleep(4);
            } else {
                System.out.println(Thread.currentThread().getName()+ " 獲取鎖失敗");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }


    public static void main(String[] args) {
        ReentrantLockDemo03 reentrantLockDemo = new ReentrantLockDemo03();
        Thread thread01 = new Thread(reentrantLockDemo, "thread01");
        Thread thread02 = new Thread(reentrantLockDemo, "thread02");
        thread01.start();
        thread02.start();
    }
}

2.2.4 newCondition() 方法說明
目前只是對newCondition()使用方式進行說明,沒有深入的分析Condition()的實現源碼。 
Condition的作用是對鎖進行更精確的控制。Condition中的await()方法相當於Object的wait()方法,Condition中的signal()方法相當於Object的notify()方法,Condition中的signalAll()相當於Object的notifyAll()方法。不同的是,Object中的wait(),notify(),notifyAll()方法是和”同步鎖”(synchronized關鍵字)捆綁使用的;而Condition是需要與”互斥鎖”/”共享鎖”捆綁使用的。

public class ProducerConsumerTest {

    private Lock lock = new ReentrantLock();

    private Condition addCondition = lock.newCondition();

    private Condition removeCondition = lock.newCondition();

    private LinkedList<Integer> resources = new LinkedList<>();

    private int maxSize;

    public ProducerConsumerTest(int maxSize) {
        this.maxSize = maxSize;
    }


    public class Producer implements Runnable {

        private int proSize;

        private Producer(int proSize) {
            this.proSize = proSize;
        }

        @Override
        public void run() {
            lock.lock();
            try {
                for (int i = 1; i < proSize; i++) {
                    while (resources.size() >= maxSize) {
                        System.out.println("當前倉庫已滿,等待消費...");
                        try {
                            addCondition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("已經生產產品數: " + i + "\t現倉儲量總量:" + resources.size());
                    resources.add(i);
                    removeCondition.signal();
                }
            } finally {
                lock.unlock();
            }

        }
    }

    public class Consumer implements Runnable {

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            while (true) {
                lock.lock();
                try {
                    while (resources.size() <= 0) {
                        System.out.println(threadName + " 當前倉庫沒有產品,請稍等...");
                        try {
                            // 進入阻塞狀態
                            removeCondition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    // 消費數據
                    int size = resources.size();
                    for (int i = 0; i < size; i++) {
                        Integer remove = resources.remove();
                        System.out.println(threadName + " 當前消費產品編號爲:" + remove);
                    }
                    // 喚醒生產者
                    addCondition.signal();
                } finally {
                    lock.unlock();
                }
            }

        }
    }

    public static void main(String[] args) throws InterruptedException {
        ProducerConsumerTest producerConsumerTest = new ProducerConsumerTest(10);
        Producer producer = producerConsumerTest.new Producer(100);
        Consumer consumer = producerConsumerTest.new Consumer();
        final Thread producerThread = new Thread(producer, "producer");
        final Thread consumerThread = new Thread(consumer, "consumer");
        producerThread.start();
        TimeUnit.SECONDS.sleep(2);
        consumerThread.start();
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章