Java 5.0之前,在协调对共享对象的访问时可以使用的机制只有synchronized和volatile。java 5.0增加了一种新的机制:ReentrantLock,用以当内置枷锁机制不适用时,作为一种可选择的高级功能。
Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,下面来看Lock接口提供了哪些方法:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
下面来看一些ReentrantLock的用法:
一.通过轮询避免死锁
关于死锁此处就不再赘述了,楼主之前写过一篇死锁的文章,有兴趣的可以移步:
http://blog.scarlettbai.com/sync-t1459676182/
下面直接看用ReentrantLoc的实现:
先构造一个对象类:
public class PersonVO {
/** 年龄 */
private int age;
/** ReentrantLock锁 */
private Lock lock = new ReentrantLock();
public int getAge() {
return age;
}
public Lock getLock() {
return lock;
}
}
下面看测试方法:
@Test
public void loopLock() throws InterruptedException {
PersonVO qian = new PersonVO();
PersonVO zhang = new PersonVO();
Thread thread1 = new Thread(() -> {
while (true) {
if (qian.getLock().tryLock()) {
try {
System.out.println("【thread1】获取到钱先生的锁");
Thread.sleep(200L);
if (zhang.getLock().tryLock()) {
try {
System.out.println("【thread1】获取到张先生的锁");
} finally {
zhang.getLock().unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
qian.getLock().unlock();
}
}
}
});
Thread thread2 = new Thread(() -> {
while (true) {
if (zhang.getLock().tryLock()) {
try {
System.out.println("【thread2】获取到张先生的锁");
Thread.sleep(33L);
if (qian.getLock().tryLock()) {
try {
System.out.println("【thread2】获取到钱先生的锁");
} finally {
qian.getLock().unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
zhang.getLock().unlock();
}
}
}
});
thread1.start();
thread2.start();
Thread.sleep(100L);
}
输出结果:
【thread1】获取到钱先生的锁
【thread2】获取到张先生的锁
【thread2】获取到张先生的锁
【thread2】获取到张先生的锁
可以看到,并没有出现死锁,如果此处用synchronized关键字来做对象锁的话,两个线程获取的锁顺序不一致是会造成死锁的。(这里代码最后一行只是为了避免主线程执行完毕两个子线程会直接终止)
二.指定尝试时间锁,防止一直阻塞
还是用上例的PersonVO对象,下面直接看测试代码:
public void lockWithTime() throws InterruptedException {
PersonVO zhang = new PersonVO();
Thread thread1 = new Thread(() -> {
zhang.getLock().lock();
try {
System.out.println("【thread1】获取到张先生的锁");
Thread.sleep(200L);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
zhang.getLock().unlock();
}
});
Thread thread2 = new Thread(() -> {
try {
if (zhang.getLock().tryLock(100L, TimeUnit.MILLISECONDS)) {
try {
System.out.println("【thread2】获取到张先生的锁");
} finally {
zhang.getLock().unlock();
}
} else {
System.out.println("【thread2】未能获取到张先生的锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
Thread.sleep(300L);
}
运行看结果:
【thread1】获取到张先生的锁
【thread2】未能获取到张先生的锁
通过运行结果可看到,线程2并没有获取到锁,在指定时间未获取到锁后继续跑完其余流程,并未造成线程阻塞。
三.可中断锁
lockInterruptibly方法能在阻塞等待获取锁的同时保持对中断的响应,所以可以通过其他线程中断锁,并由当前线程执行中断处理,看如下代码:
@Test
public void lockInterruptiblyTest() throws Exception {
final Lock lock = new ReentrantLock();
lock.lock();
Thread t1 = new Thread(() -> {
try {
lock.lockInterruptibly();
System.out.println("获取到锁");
} catch (InterruptedException e) {
System.out.println("执行中断处理.");
}
});
t1.start();
Thread.sleep(100L);
t1.interrupt();
Thread.sleep(1000L);
}
运行看结果:
执行中断处理.
欢迎关注个人博客:http://blog.scarlettbai.com