ReentrantLock类与Condition类的结合使用

/ 多线程 / 没有评论 / 320浏览

我们在上一篇简单介绍了ReentrantLock类的基本使用,也就是获取锁,与释放锁。那如果我们要实现wait()和notify()等待通知的功能在ReentrantLock类中我们应该怎么办呢。这时我们就要借助一个新类了,它就是Condition类。Condition类也是JDK1.5以后新增的类。它可以实现多路通知功能,也就是说在一个Lock对象中可以创建多个Condition类相当于有多个锁对象,通知的时候可以选择性的进行线程通知,而不是notify()那样是由CPU随机决定通知的是哪个线程,Condition类使它在线程调度上更加灵活。下面我们看一下具体的事例。

/**
* 用户登录
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:35
* @since 1.0.0
*/
public class Userinfo {

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

public void await() {
try {
lock.lock();
System.out.println(String.format("线程开始\tthread: %s", Thread.currentThread().getName()));
condition.await();
System.out.println(String.format("线程结束\tthread: %s", Thread.currentThread().getName()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void signal() {
lock.lock();
System.out.println(String.format("线程恢复\tthread: %s", Thread.currentThread().getName()));
condition.signal();
lock.unlock();
}
}
/**
* 管理用户请求
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:44
* @since 1.0.0
*/
public class RequestAdmin extends Thread {

private Userinfo userinfo;


public RequestAdmin(Userinfo userinfo) {
this.userinfo = userinfo;
}

@Override
public void run() {
userinfo.await();
}
}
/**
* 用户请求
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:44
* @since 1.0.0
*/
public class RequestUser extends Thread {

private Userinfo userinfo;

public RequestUser(Userinfo userinfo) {
this.userinfo = userinfo;
}

@Override
public void run() {
userinfo.signal();
}
}
/**
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-20 13:35
* @since 1.0.0
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
Userinfo userinfo = new Userinfo();
RequestAdmin requestAdmin = new RequestAdmin(userinfo);
RequestUser requestUser = new RequestUser(userinfo);
requestAdmin.start();
Thread.sleep(1000);
for (int i = 3; i > 0; i--) {
System.out.println(String.format("倒计时:%s", i));
Thread.sleep(1000);
}
requestUser.start();
}
}
线程开始	thread: Thread-0
倒计时:3
倒计时:2
倒计时:1
线程恢复	thread: Thread-1
线程结束	thread: Thread-0

我们用ReentrantLock和Condition类成功实现了wait()和notify()暂停与通知的功能。使用还是比较简单,和以前的wait()、notify()方法使用基本一致。

condition.await(); // 相当于wait()方法
condition.signal();// 相当于notify()方法

我们知道用notifyAll()方法可以恢复暂停的所有线程,同样Condition类中也有同样的方法实现了该逻辑,只是方法不叫notifyAll()而是叫signalAll()方法。请看下面事例。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* 用户登录
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:35
* @since 1.0.0
*/
public class Userinfo {

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

public void await1() {
try {
lock.lock();
System.out.println(String.format("await1线程开始\tthread: %s", Thread.currentThread().getName()));
condition.await();
System.out.println(String.format("await1线程结束\tthread: %s", Thread.currentThread().getName()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void await2() {
try {
lock.lock();
System.out.println(String.format("await2线程开始\tthread: %s", Thread.currentThread().getName()));
condition.await();
System.out.println(String.format("await2线程结束\tthread: %s", Thread.currentThread().getName()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void signalAll() {
lock.lock();
System.out.println(String.format("恢复所有线程\tthread: %s", Thread.currentThread().getName()));
condition.signalAll();
lock.unlock();
}
}
/**
* 管理用户请求
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:44
* @since 1.0.0
*/
public class RequestAdmin extends Thread {

private Userinfo userinfo;


public RequestAdmin(Userinfo userinfo) {
this.userinfo = userinfo;
}

@Override
public void run() {
userinfo.await1();
}
}
/**
* 用户请求
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:44
* @since 1.0.0
*/
public class RequestUser extends Thread {

private Userinfo userinfo;

public RequestUser(Userinfo userinfo) {
this.userinfo = userinfo;
}

@Override
public void run() {
userinfo.await2();
}
}
/**
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-20 13:35
* @since 1.0.0
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
Userinfo userinfo = new Userinfo();
RequestAdmin requestAdmin = new RequestAdmin(userinfo);
RequestUser requestUser = new RequestUser(userinfo);
requestAdmin.start();
requestUser.start();
Thread.sleep(1000);
for (int i = 3; i > 0; i--) {
System.out.println(String.format("倒计时:%s", i));
Thread.sleep(1000);
}
userinfo.signalAll();
}
}
await1线程开始	thread: Thread-0
await2线程开始	thread: Thread-1
倒计时:3
倒计时:2
倒计时:1
恢复所有线程	thread: main
await1线程结束	thread: Thread-0
await2线程结束	thread: Thread-1

我们看因为调用了signalAll()方法,所以所有暂停的线程都被恢复了。这和notifyAll()方法一样,没什么好说的。但如果我们想要实现恢复指定的线程那应该怎么办呢?我们知道调用notifyAll()方法恢复哪个线程是由CPU决定的,我们程序是控制不了的,这时有人可能会想到设置线程的优先级来实现让指定的线程优先执行。但这还不是绝对的。设置优先级也是说明那个线程获取执行的概率比较大,还是不能保证百分之百执行的。虽然用notifyAll()方法没有什么简单的办法让指定的线程恢复执行,但在Condition类中确可以很方便的实现此功能,这也是Condition类可以实现多路通知功能的体现。下面我们来演示一下用Condition类怎么实现我们上述的需求。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* 用户登录
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:35
* @since 1.0.0
*/
public class Userinfo {

private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();

public void await1() {
try {
lock.lock();
System.out.println(String.format("await1线程开始\tthread: %s", Thread.currentThread().getName()));
condition1.await();
System.out.println(String.format("await1线程结束\tthread: %s", Thread.currentThread().getName()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void await2() {
try {
lock.lock();
System.out.println(String.format("await2线程开始\tthread: %s", Thread.currentThread().getName()));
condition2.await();
System.out.println(String.format("await2线程结束\tthread: %s", Thread.currentThread().getName()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void signalAll1() {
lock.lock();
System.out.println(String.format("恢复所有线程\tthread: %s", Thread.currentThread().getName()));
condition1.signalAll();
lock.unlock();
}

public void signalAll2() {
lock.lock();
System.out.println(String.format("恢复所有线程\tthread: %s", Thread.currentThread().getName()));
condition2.signalAll();
lock.unlock();
}
}
/**
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-20 13:35
* @since 1.0.0
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
Userinfo userinfo = new Userinfo();
RequestAdmin requestAdmin = new RequestAdmin(userinfo);
RequestUser requestUser = new RequestUser(userinfo);
requestAdmin.start();
requestUser.start();
Thread.sleep(1000);
for (int i = 3; i > 0; i--) {
System.out.println(String.format("倒计时:%s", i));
Thread.sleep(1000);
}
userinfo.signalAll1();
}
}
await1线程开始	thread: Thread-0
await2线程开始	thread: Thread-1
倒计时:3
倒计时:2
倒计时:1
恢复所有线程	thread: main
await1线程结束	thread: Thread-0

我们看这时就实现了我们上述的需求。用Condition类可以唤醒我们指定的线程,确实比notify()更简单更方便。