ReentrantLock锁的底层实现已经阐述过了,那么如何使用,本文进行下样例展示,主要说3个:1. lock及中断,2. 申请等待时间,3、公平锁
/**
* @Description :
* @author : Erick
* @version : 1.0
* @time :2018-9-25
*/
public class ReentrantLockThread implements Runnable{
private static ReentrantLock reentrantLock = new ReentrantLock();
private int i = 0;
public void run() {
for (int j = 0; j < 10; j++) {
//上锁
reentrantLock.lock();
try {
i++;
System.out.println("当前线程"+Thread.currentThread().getName()+"输出 i :" + i);
} finally {
//释放锁
reentrantLock.unlock();
}
}
}
}
上例中一个实现一个线程,并定义ReentrantLock,与synchronized不同的是要显示的lock上锁并手动释放锁,是一套缺一不可,将上锁和释放锁注释然后调用会出现线程交替执行的情况,我们写个测试类
/**
* @Description :
* @author : Erick
* @version : 1.0
* @time :2018-9-25
*/
public class ReentrantLockTest {
public static void main(String[] args) {
//定义两个线程
ReentrantLockThread reentrantLockThread1 = new ReentrantLockThread();
ReentrantLockThread reentrantLockThread2 = new ReentrantLockThread();
//调用run方法执行其实就是调用的接口方法,主线程运行在打印线程名称时都是main,
//而使用Thread.start()则是新起一个线程进行调用,更新执行标识
// reentrantLockThread1.run();
// reentrantLockThread2.run();
Thread thread1 = new Thread(reentrantLockThread1);
Thread thread2 = new Thread(reentrantLockThread2);
thread1.start();
thread2.start();
}
}
为方便展示只循环了10次注释掉上锁和释放锁的代码,执行结果为:
当前线程Thread-0输出 i :1
当前线程Thread-1输出 i :1
当前线程Thread-0输出 i :2
当前线程Thread-1输出 i :2
当前线程Thread-0输出 i :3
当前线程Thread-1输出 i :3
当前线程Thread-0输出 i :4
当前线程Thread-1输出 i :4
当前线程Thread-0输出 i :5
当前线程Thread-1输出 i :5
当前线程Thread-0输出 i :6
当前线程Thread-1输出 i :6
当前线程Thread-0输出 i :7
当前线程Thread-1输出 i :7
当前线程Thread-0输出 i :8
当前线程Thread-1输出 i :8
当前线程Thread-1输出 i :9
当前线程Thread-0输出 i :9
当前线程Thread-1输出 i :10
当前线程Thread-0输出 i :10
若加上锁,则是一个先执行完另外一个再执行,获取到锁后,第二个线程在请求时发现锁被占用则加入到队列中等待,所以等第一个线程执行完成后,第二个线程才会执行,执行结果如下:
当前线程Thread-0输出 i :1
当前线程Thread-0输出 i :2
当前线程Thread-0输出 i :3
当前线程Thread-0输出 i :4
当前线程Thread-0输出 i :5
当前线程Thread-0输出 i :6
当前线程Thread-0输出 i :7
当前线程Thread-0输出 i :8
当前线程Thread-0输出 i :9
当前线程Thread-0输出 i :10
当前线程Thread-1输出 i :1
当前线程Thread-1输出 i :2
当前线程Thread-1输出 i :3
当前线程Thread-1输出 i :4
当前线程Thread-1输出 i :5
当前线程Thread-1输出 i :6
当前线程Thread-1输出 i :7
当前线程Thread-1输出 i :8
当前线程Thread-1输出 i :9
当前线程Thread-1输出 i :10
中断是两个线程分表获取对方需要的数据时即死锁,为了防止线程一直等待浪费资源,ReentrantLock提供了中断响应接口支持,及可以手动调用线程中断来强制让一个线程中断并返回异常信息,而另外一个线程则会拿到锁继续执行。
/**
* @Description :中断响应
* @author : Erick
* @time :2018-9-25
*/
public class ReentrantLockInterrupt implements Runnable{
/**
* 定义两个锁,模拟响应中断
*/
private static ReentrantLock reentrantLock1 = new ReentrantLock();
private static ReentrantLock reentrantLock2 = new ReentrantLock();
/**
* 增加一个标识表名哪个设置中断响应所
*/
private String lockFlag;
public ReentrantLockInterrupt(String lockFlag) {
this.lockFlag = lockFlag;
}
public void run() {
try {
//lockFlag = 1则先设置reentrantLock1中断锁
//否则reentrantLock2 设置中断锁
if ("1".equals(lockFlag)){
reentrantLock1.lockInterruptibly();
Thread.sleep(500);
reentrantLock2.lockInterruptibly();
System.out.println("reentrantLock1执行");
}else {
reentrantLock2.lockInterruptibly();
Thread.sleep(500);
reentrantLock1.lockInterruptibly();
System.out.println("reentrantLock2执行");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//要下判断是否获取当前线程的锁,若是则释放掉
if (reentrantLock1.isHeldByCurrentThread()){
reentrantLock1.unlock();
System.out.printf("释放当前锁reentrantLock1");
}
if (reentrantLock2.isHeldByCurrentThread()){
reentrantLock2.unlock();
System.out.println("释放当前锁reentrantLock2");
}
}
}
}
上例再执行是会出现死锁现象,因两个线程一个走if一个走else,都会拿到对方需要的锁谁都不会释放,直到一个线程中断另一个才会执行。
/**
* @Description :
* @author : Erick
* @time :2018-9-25
*/
public class ReentrantLockInterruptTest {
public static void main(String[] args) throws InterruptedException {
ReentrantLockInterrupt reentrantLockInterrupt1 = new ReentrantLockInterrupt("1");
ReentrantLockInterrupt reentrantLockInterrupt2 = new ReentrantLockInterrupt("2");
//这里新定义两个线程Thread为了使用interrupt方法对线程进行中断
Thread thread1 = new Thread(reentrantLockInterrupt1);
Thread thread2 = new Thread(reentrantLockInterrupt2);
thread1.start();
thread2.start();
Thread.sleep(1000);
thread2.interrupt();
}
}
测试用例中thread1请求if,thread2请求else,启动两个线程同时获取锁出现死锁,然后再休眠1s,确保都拿到锁了方便演示,执行情况如下:
释放当前锁reentrantLock2
java.lang.InterruptedException
reentrantLock1执行
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at com.erick.study.reentrantlock.thread.ReentrantLockInterrupt.run(ReentrantLockInterrupt.java:41)
at java.lang.Thread.run(Thread.java:745)
释放当前锁reentrantLock1释放当前锁reentrantLock2
线程2被中断抛出异常,线程1拿到锁可以顺利执行。
有两种方式:1、无参即尝试获取锁获取不到则直接返回,2、设置等待时间超过时间后还是无法获取到则返回,否则获取锁执行。
/**
* @author : Erick
* @version : 1.0
* @Description :
* @time :2018-9-30
*/
public class ReentrantLockTryLock implements Runnable{
private ReentrantLock reentrantLock = new ReentrantLock();
private int i = 0;
public void run() {
for (int j = 0; j < 10; j++) {
try {
//设置等待时间为1秒
if (reentrantLock.tryLock(1,TimeUnit.SECONDS)){
i++;
System.out.println(Thread.currentThread().getName()+"当前值:"+i);
//设置休眠时间
Thread.sleep(500);
}else {
System.out.println(Thread.currentThread().getName()+"无法获取锁");
}
} catch (Exception e) {
e.printStackTrace();
System.out.printf("异常"+e.getMessage());
} finally {
if (reentrantLock.isHeldByCurrentThread()){
reentrantLock.unlock();
}
}
}
}
}
tryLock不设置等待时间,执行都能获取到锁交替运行,而加上等待时间后会等待一段时间若还是获取不到锁则打印无法获取锁,测试类
/**
* @author : Erick
* @version : 1.0
* @Description :
* @time :2018-9-30
*/
public class ReetrantLockTryLockTest {
public static void main(String[] args) {
ReentrantLockTryLock reentrantLockTryLock = new ReentrantLockTryLock();
Thread thread1 = new Thread(reentrantLockTryLock);
Thread thread2 = new Thread(reentrantLockTryLock);
thread1.setName("thread1");
thread2.setName("thread2");
thread1.start();
thread2.start();
}
}
公平锁是按照进入队列的先后顺序执行的,因为Demo比较简单,每次执行的结果也不一样。
/**
* @author : Erick
* @version : 1.0
* @Description :
* @time :2018-9-30
*/
public class ReentrantLockFair implements Runnable{
//实例化指定公平锁方式
private ReentrantLock reentrantLock = new ReentrantLock(true);
public void run() {
for (int j = 0; j < 10000; j++) {
try {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"获取锁");
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
}
}
比较简单获取锁,然后输出运行的线程名称,测试类
/**
* @author : Erick
* @version : 1.0
* @Description :
* @time :2018-9-30
*/
public class ReentrantLockFairTest {
public static void main(String[] args) {
ReentrantLockFair reentrantLockFair = new ReentrantLockFair();
Thread thread1 = new Thread(reentrantLockFair);
Thread thread2 = new Thread(reentrantLockFair);
thread1.start();
thread2.start();
}
}
执行结果,粘贴部分结果
Thread-0获取锁
Thread-0获取锁
Thread-0获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
Thread-0获取锁
Thread-1获取锁
以上是整理的关于重入锁ReentrantLock的几个简单的Demo,如有不妥之处也请指正。
源代码可以从GitHub或者码云上下载。