JAVA线程协作:ReentrantLock重入锁
2018-01-25 本文已影响0人
barry_di
一、ReentrantLock
我们都知道锁是为了保护临界区资源被多线程并发访问时的安全性。 而 ReentrantLock是JAVA提供的完全可以替代Synchronized的方案。在JDK6.0以前Synchronized的执行效率远远比ReentrantLock的执行效率差。而在JDK6.0以后对Synchronized的优化后,ReentrantLock与Synchronized的执行效率差距不大。
二、ReentrantLock的lock与unlock
- ReentrantLock的lock与unlock是一套灵活的保护临界区资源的方法。我们需要注意如果在使用lock的时候,我们必须要执行unlock操作。如果我们只调用了lock而没有调用unlock的释放锁的情况下,会造成其他线程获取不到锁,而导致阻塞。
public class ReentrantLockDemo implements Runnable{
public ReentrantLock lock = new ReentrantLock();
public static int count = 0;
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
try {
//通过重入锁,防止并发增加Count的值
lock.lock();
count++;
} catch (Exception e) {
e.printStackTrace();
} finally {
//使用重入锁,一定要解锁,如果没有解锁会导致其他线程不可以访问。
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();
Thread t1 = new Thread(reentrantLockDemo);
Thread t2 = new Thread(reentrantLockDemo);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Over Count="+count);
}
}
- ReentrantLock有一种特性就是ReentrantLock的锁可以反复的进入,而这种反复的进入仅限于统一线程下,也就是说我们可以将代码改成下面这种形式。但是我们要记住一点,重复进入锁调时,一定要释放锁.
public class ReentrantLockDemo implements Runnable{
public ReentrantLock lock = new ReentrantLock();
public static int count = 0;
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
try {
//通过重入锁,防止并发增加Count的值
lock.lock();
//重复进入锁
lock.lock();
count++;
} catch (Exception e) {
e.printStackTrace();
} finally {
//使用重入锁,一定要解锁,如果没有解锁会导致其他线程不可以访问。
lock.unlock();
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();
Thread t1 = new Thread(reentrantLockDemo);
Thread t2 = new Thread(reentrantLockDemo);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Over Count="+count);
}
}
二、ReentrantLock的lockInterruptibly
我们知道使用Synchronized的时候,如果锁被其他线程获取。而当前线程只能等在其他线程释放锁,并且获得锁才会执行,没有获得锁时处于阻塞状态。而ReentrantLock提供了lockInterruptibly来优化这种情况。就是通过中断锁。例如我们在死锁的情况下可以通过中断其中一个线程的锁来解决这种死锁的状态。
public class IntReentrantLock implements Runnable {
public ReentrantLock r1 = new ReentrantLock();
public ReentrantLock r2 = new ReentrantLock();
int lock = 0;
//控制死锁
public IntReentrantLock(int lock) {
this.lock = lock;
}
@Override
public void run() {
try {
if(lock == 1){
//使用lockInterruptibly表明当前的锁可以被终止.
r1.lockInterruptibly();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
r2.lockInterruptibly();
}else{
r2.lockInterruptibly();
try {
//线程在睡眠的时候被中断,会抛出InterruptedException异常
Thread.sleep(5000);
} catch (InterruptedException e) {
}
r1.lockInterruptibly();
}
}catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(r1.isHeldByCurrentThread()){
r1.unlock();
}
if(r2.isHeldByCurrentThread()){
r2.unlock();
}
System.out.println("Thread :"+Thread.currentThread().getId()+"-"+Thread.currentThread().getName()+":当前线程退出");
}
}
public static void main(String[] args) throws InterruptedException {
IntReentrantLock testLock1 = new IntReentrantLock(1);
IntReentrantLock testLock2 = new IntReentrantLock(2);
Thread t1 = new Thread(testLock1,"线程1");
Thread t2 = new Thread(testLock2,"线程2");
t1.start();
t2.start();
Thread.sleep(1000);
//中断线程2
t2.interrupt();
}
}
输出结果:
Thread :11-线程2:当前线程退出
Thread :10-线程1:当前线程退出
上面的代码是通过模拟死锁的状态。testLock1调用的时候需要用到testLock2 的锁,testLock2 调用的时候需要用到testLock1的锁,而双方有拿着各自的锁。导致死锁。我们通过对线程t2进行终端从而解决这种死锁的状态。
三、ReentrantLock的trylock
除了中断锁通知的这种操作处理死锁的这种情况,我们也可以通过设置获取锁的等待时间来进行处理。
public class TryLock implements Runnable {
public ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
//等待获取锁
if (lock.tryLock(3, TimeUnit.SECONDS)) {
System.out.println("get Lock Success");
Thread.sleep(5000);
} else {
System.out.println("get Lock Faild");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
System.out.println("releas Lock Success");
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
TryLock tryLock = new TryLock();
Thread t1 = new Thread(tryLock);
Thread t2 = new Thread(tryLock);
t1.start();
t2.start();
t1.join();
t2.join();
}
}
输出结果:
get Lock Success
get Lock Faild
releas Lock Success
四、ReentrantLock的condition
我们在使用Synchronized的时候,当某些时候需要在双线程的协同处理的时候会使用到了wait()和notify()进行处理这种协同处理。而ReentrantLock也提供了这种协同处理就是await()和signal()。
public class ReentrantLockCondition implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();
@Override
public void run() {
try {
lock.lock();
condition.await();
System.out.println("out lock wait");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockCondition reentrantLockCondition= new ReentrantLockCondition();
Thread t1 = new Thread(reentrantLockCondition);
t1.start();
Thread.sleep(2000);
lock.lock();
//唤醒t1线程的等待
condition.signal();
System.out.println("signal Thread t1");
//释放当前的主线程的锁,如果没有释放则唤醒的t1是不可能获取到锁,而t1唤醒后获取不到锁会处理阻塞状态
lock.unlock();
}
}
输出结果:
signal Thread t1
out lock wait