Java多线程编程-ReentrantLock使用及其源码解析
- ReentrantLock简单使用
-
- ReentrantLock实现等待通知
在java多线程开发中,我们可以使用 synchronized关键字来实现线程之间的同步互斥,我们也可以使用 ReentrantLock实现更多的功能,如嗅探锁定。多路分支通知等功能,使用上会比synchronized更加灵活。
ReentrantLock简单使用
可重入的互斥Lock,具有与使用sync方法和语句访问的隐式监视器锁相同的基本行为和语义。
由上一次成功锁定但尚未解锁的线程所有。当该锁不属于另一个线程时,调用lock的线程将返回,并成功获取该锁。如果当前线程已经拥有该锁,则该方法将立即返回。可以使用方法 isHeldByCurrentThread和getHoldCount进行检查。
public class ReentrantLockV1Main {
public static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
Thread thread01 = new Thread(() -> {
reentrantLock.lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + (i + 1));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}, "thread01");
Thread thread02 = new Thread(() -> {
reentrantLock.lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + (i + 1));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}, "thread02");
Thread thread03 = new Thread(() -> {
reentrantLock.lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + (i + 1));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}, "thread03");
thread01.start();
thread02.start();
thread03.start();
}
}
运行结果:
使用lock方法获取锁,使用unlock方法释放锁。每个线程同步执行。
ReentrantLock实现等待通知
我们知道synchronized和wait/notify可以实现等待通知模式,ReentrantLock也可以实现,但是要借助Condition对象。在一个lock对象里面可以创建多个condition实例,线程对象可以注册在指定的condition中,从而可以有选择性的进行线程的通知。在调度线程上更加灵活。
在没有获得锁的情况下,进行wait操作会报错:
public class ReentranLockObj1 {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitFun() {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程类:
public class ReentranLockObj1T1 extends Thread {
private ReentranLockObj1 reentranLockObj1;
public ReentranLockObj1T1(ReentranLockObj1 reentranLockObj1) {
this.reentranLockObj1 = reentranLockObj1;
}
@Override
public void run() {
reentranLockObj1.waitFun();
}
}
运行结果:
我们看看该类的Condition接口源码:
public interface Condition {
//使当前线程等待,直到发出信号或被中断
void await() throws InterruptedException;
//使当前线程等待,直到发出信号
void awaitUninterruptibly();
//使当前线程等待,直到发出信号或被中断或经过指定的等待时间为止
long awaitNanos(long nanosTimeout) throws InterruptedException;
//使当前线程等待,直到发出信号或被中断,或者经过指定的等待时间
boolean await(long time, TimeUnit unit) throws InterruptedException;
//使当前线程等待,直到发出信号或被中断,或者经过指定的截止时间
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒一个等待的线程
void signal();
//唤醒所有的等待线程
void signalAll();
}
我们只需要将ReentranLockObj1 waitFun方法中添加lock即可,修改如下:
public void waitFun() {
lock.lock();
try {
System.out.println("current thread name :" + Thread.currentThread().getName());
condition.await();
System.out.println("current thread name :" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
看看运行结果:
await方法后面的语句没有输入到控制台,处于等待状态。
进行通知唤醒:
public class ReentranLockObj2 {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
/**
* 等待
*/
public void waitFun() {
lock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName());
condition.await();
System.out.println("current thread name:" + Thread.currentThread().getName());
} catch (IllegalMonitorStateException | InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 唤醒
*/
public void notifyFun() {
lock.lock();
try {
condition.signal();
} catch (IllegalMonitorStateException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ReentranLockObj1T2Main {
public static void main(String[] args) throws InterruptedException {
ReentranLockObj2 reentranLockObj2 = new ReentranLockObj2();
ReentranLockObj2T2 reentranLockObj2T2 = new ReentranLockObj2T2(reentranLockObj2);
reentranLockObj2T2.start();
Thread.sleep(1000);
reentranLockObj2.notifyFun();
}
}
运行结果:
打印第一句,1秒之后打印第二句,使用signal()唤醒。唤醒所有等待中的线程,使用signalAll()
public class ReentranLockObj3 {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitFun1() {
lock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName());
condition.await();
System.out.println("current thread name:" + Thread.currentThread().getName());
} catch (IllegalMonitorStateException | InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void waitFun2() {
lock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName());
condition.await();
System.out.println("current thread name:" + Thread.currentThread().getName());
} catch (IllegalMonitorStateException | InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void notifyAllFun() {
lock.lock();
try {
condition.signalAll();
} catch (IllegalMonitorStateException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
线程1:
public class ReentranLockObj3T1 extends Thread {
private ReentranLockObj3 reentranLockObj3;
public ReentranLockObj3T1(ReentranLockObj3 reentranLockObj3, String threadName) {
this.reentranLockObj3 = reentranLockObj3;
this.setName(threadName);
}
@Override
public void run() {
reentranLockObj3.waitFun1();
}
}
线程2:
public class ReentranLockObj3T2 extends Thread {
private ReentranLockObj3 reentranLockObj3;
public ReentranLockObj3T2(ReentranLockObj3 reentranLockObj3, String threadName) {
this.reentranLockObj3 = reentranLockObj3;
this.setName(threadName);
}
@Override
public void run() {
reentranLockObj3.waitFun2();
}
}
运行结果:
先输出上面两行,等5分钟通过调用signalAll进行唤醒所有等待中的线程。
如果想单独唤醒一个,该怎么处理呢? 我们需要创建多个condition进行处理了。通过condition唤醒部分线程,可以有效提高程序运行效率。
public class ReentranLockObj4 {
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
public void waitFunCondition1() {
lock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName());
condition1.await();
System.out.println("current thread name:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void waitFunCondition2() {
lock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName());
condition2.await();
System.out.println("current thread name:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void notifyFunCondition1() {
lock.lock();
try {
condition1.signalAll();
} catch (IllegalMonitorStateException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void notifyFunCondition2() {
lock.lock();
try {
condition2.signalAll();
} catch (IllegalMonitorStateException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
线程1:
public class ReentranLockObj4T1 extends Thread {
private ReentranLockObj4 reentranLockObj4;
public ReentranLockObj4T1(ReentranLockObj4 reentranLockObj4, String threadName) {
this.reentranLockObj4 = reentranLockObj4;
this.setName(threadName);
}
@Override
public void run() {
reentranLockObj4.waitFunCondition1();
}
}
线程2:
public class ReentranLockObj4T2 extends Thread {
private ReentranLockObj4 reentranLockObj4;
public ReentranLockObj4T2(ReentranLockObj4 reentranLockObj4, String threadName) {
this.reentranLockObj4 = reentranLockObj4;
this.setName(threadName);
}
@Override
public void run() {
reentranLockObj4.waitFunCondition2();
}
}
运行:
public class ReentranLockObj4Main {
public static void main(String[] args) throws InterruptedException {
ReentranLockObj4 reentranLockObj4 = new ReentranLockObj4();
ReentranLockObj4T1 reentranLockObj4T1 = new ReentranLockObj4T1(reentranLockObj4, "thread01");
reentranLockObj4T1.start();
ReentranLockObj4T2 reentranLockObj4T2 = new ReentranLockObj4T2(reentranLockObj4, "thread02");
reentranLockObj4T2.start();
Thread.sleep(5000);
reentranLockObj4.notifyFunCondition1();
}
}
运行结果:
有运行可以看出,当线程1唤醒的时候,调用线程1的wait后的方法,线程2没有调用,就还在等待中,程序没有结束。由此可以得出结论,ReentrantLock可以唤醒指定种类的线程。
总结一下:
1.Object类中的wait相当于Condition中的await
2.Object类中的wait(long timeout) 相当于Condition中的await(long timeout,TimeUnit unit)
3.Object类中的notify相当于Condition中的signal
4.Object类中的notifyAll相当于Condition中的signalAll