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);
}
}