重入锁ReentrantLock

1. 介绍


  • ReentrantLock顾名思义就是锁可以重入,ReentrantLock持有一个所计数器,当已持有所的线程再次获得该锁时计数器值加1,每调用一次lock.unlock()时所计数器值减一,直到所计数器值为0,此时线程释放锁。
    例如:一个线程持有锁,state=1,如果它再次调用lock方法,那么他将继续拥有这把锁,state=2。当前可重入锁要完全释放,调用了多少次lock方法,还得调用等量的unlock方法来完全释放锁。

2. API使用


  • lock()\unlock() 加锁与释放锁

public class ReentrantLockTest {
    private Lock lock = new ReentrantLock();

    public void testReentrantLock() throws InterruptedException {
        // 线程获得锁
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " get lock");
            Thread.sleep(100L);
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " get lock again");
            }finally {
                // 线程释放锁
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + " release lock");
            }
        } finally {
            // 线程释放锁
            lock.unlock();
            System.out.println(Thread.currentThread().getName() + " release lock again");
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        final ReentrantLockTest test2 = new ReentrantLockTest();
        Thread thread = new Thread(new Runnable(){
            public void run() {
                try {
                    test2.testReentrantLock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A");
        thread.start();
    }
}
  • 测试锁与超时

  • lock(), 拿不到lock就不罢休,不然线程就一直block。 比较无赖的做法。
  • tryLock(),马上返回,拿到lock就返回true,不然返回false。 比较潇洒的做法。
  • tryLock(long time, TimeUnit unit),带时间限制拿不到lock,就等一段时间,超时返回false。比较聪明的做法。
  • lockInterruptibly(),线程在sleep或wait,join, 此时如果别的进程调用此进程的 interrupt()方法,此线程会被唤醒并被要求处理InterruptedException。

代码示例:

public void lockTest() {
    // 当前线程在锁可用时直接获得锁,锁不可用时阻塞当前线程  
    lock.lock();
    try {
        System.out.println(Thread.currentThread().getName() + " lock get lock");
        long beginTime = System.currentTimeMillis();
        while (System.currentTimeMillis() - beginTime < 1000) {}
    } finally {
        lock.unlock();
        System.out.println(Thread.currentThread().getName() + " lock release lock");
    }
}

public void tryLockTest() throws InterruptedException {
    long beginTime = System.currentTimeMillis();  
    while(System.currentTimeMillis() - beginTime <100) {}  
    // 当前线程尝试获得锁,如果获得锁返回true,否则返回false  
    if(lock.tryLock()) {  
        try{  
            System.out.println(Thread.currentThread().getName() + " tryLock get lock");  
        } finally {  
            lock.unlock();  
            System.out.println(Thread.currentThread().getName() + " tryLock release lock");  
        }  
    } else {  
        System.out.println(Thread.currentThread().getName() + " tryLock can not get lock");  
    }  
}
  • lock方法不能被中断。如果一个线程在等待获得一个锁时被中断,中断线程在获得锁之前会一直处于 阻塞状态。如果出现死锁,那么lock方法就无法被终止。但是tryLock(long time, TimeUnit unit)和lockInterruptibly方法在申请锁的过程中是可以被中断的。
public void tryLockInterruptTest() {  
    long beginTime = System.currentTimeMillis();  
    while(System.currentTimeMillis() - beginTime <100) {}  
    try {  
        //会抛出InterruptedException异常,需要手动处理线程中断情况。lock.lockInterruptibly()一样
        if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {  
            try{  
                System.out.println(Thread.currentThread().getName() + " tryLock get lock");  
            }finally {  
              lock.unlock();  
            }  
        }  
    } catch (InterruptedException e) {  
        // TODO Auto-generated catch block  
        System.out.println(Thread.currentThread().getName() + " was interrupted");  
    }  
 }  
  • 条件对象

  • 通常,线程进入临界区,却发现在某一条件满足之后才能执行,条件对象就是用来管理那些已经获得了锁,但是却不能做有用工作的线程。
  • 一个锁对象可以有一个或多个相关的条件对象,我们可用lock.newCondition()方法获得一个条件对象。
  • 与对象的wait()/notify()作用一样

下面我们直接通过代码来学习条件对象的使用。
以下代码实现的是简单的生产者和消费者案例:

package com.jd.coutdownlatch;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest3 {
    private ReentrantLock myLock = new ReentrantLock();

    private Condition condition = myLock.newCondition();

    private List listBuffer = new ArrayList();

    private volatile boolean runFlag = true;

    /**
     * 生产者 生产数据
     */
    public void produce() {
        int i = 0;
        while (runFlag) {
            myLock.lock();
            try {
                // 生产者检查容器中是否有数据,如果容器中有数据则生产者等待
                // 如果容器中没有数据则生产数据放入容器中并通知消费者
                if (listBuffer.size() > 0) {
                    try {
                        // 调用await()方法,生产者线程阻塞并释放锁,之后进入该条件的等待集中
                        // 直到消费者调用signalAll()方法之后,生产者线程解除阻塞并重新竞争锁
                        // 生产者线程获得锁之后,重新开始从被阻塞的地方继续执行程序
                        condition.await();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(Thread.currentThread().getName() + " add Integer");
                    listBuffer.add(i++);
                    // 生产者线程调用signalAll()方法,通知消费者线程容器中有数据
                    condition.signalAll();
                }
            } finally {
                myLock.unlock();
            }
        }
    }

    /**
     * 消费者 读取数据
     */
    public void consume() {
        while (runFlag) {
            myLock.lock();
            try {
                // 消费者检查容器中是否有数据,如果没有数据消费者等待
                // 如果容器中有数据则读取数据,读完之后通知生产者
                if (listBuffer.size() == 0) {
                    try {
                        // 同生产者线程一样,消费者线程调用await()方法阻塞并进入该条件等待集中
                        condition.await();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(Thread.currentThread().getName() + " get Integer");
                    long beginTime = 0;
                    System.out.println(listBuffer.remove(0));
                    beginTime = System.currentTimeMillis();
                    while (System.currentTimeMillis() - beginTime < 100) {
                    }
                    // 消费者线程调用signalAll()方法,通知生产者生产数据
                    condition.signalAll();
                }
            } finally {
                myLock.unlock();
            }
        }
    }


    public boolean isRunFlag() {
        return runFlag;
    }

    public void setRunFlag(boolean runFlag) {
        this.runFlag = runFlag;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        final ReentrantLockTest3 test = new ReentrantLockTest3();

        Thread produce = new Thread(new Runnable() {
            public void run() {
                test.produce();
            }
        }, "A");

        Thread consume = new Thread(new Runnable() {
            public void run() {
                test.consume();
            }
        }, "B");

        produce.start();
        consume.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        test.setRunFlag(false);
    }

}

你可能感兴趣的:(重入锁ReentrantLock)