Condition是JDK1.5之后出现的接口,用来解决线程之间的通信问题。 通过接口Lock的newCondition方法来创建一个Condition的实例对象 例如: Lock lock = new ReentrantLock(); Condition con = lock.newCondition(); Condition通过调用await()方法,和single()/singleAll()方法来阻塞和唤醒线程。与Object中的wait()和notify()/notifyAll()类似。都是用来解决线程之间的通信问题。 await()必须用在lock的包围中 首先介绍一下Lock的基本用法。//在介绍Condition之前,先介绍一下Lock的基本用法
//这里有一个资源类,有一个篮子,打算创建俩个线程,一个线程向篮子里放鸡蛋,一个线程从篮子里拿鸡蛋
//输出要求:
//放鸡蛋
//拿鸡蛋
//放鸡蛋
//拿鸡蛋...往复
class Plant{
//创建一个篮子
private ArrayList<String> plant = new ArrayList<>();
//创建锁
Lock lock = new ReentrantLock();
//放鸡蛋
public void put(){
while(true){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"正在放鸡蛋...");
plant.add("egg");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"放入了一个鸡蛋");
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
//拿鸡蛋
public void get(){
while(true){
lock.lock();
try{
if(plant.size()!=0){
System.out.println(Thread.currentThread().getName()+"正在拿鸡蛋...");
//等待1秒钟
Thread.sleep(1000);
plant.remove("egg");
System.out.println(Thread.currentThread().getName()+"拿走了一个鸡蛋");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
}//创建测试类
public class Test{
public static void main(String[] args){
Plant p = new Plant();
new Thread(new Runnable() {
@Override
public void run() {
p.put();
}
},"放鸡蛋线程").start();
new Thread(new Runnable() {
@Override
public void run() {
p.get();
}
},"拿鸡蛋线程").start();
}
}//运行结果
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋
拿鸡蛋线程正在拿鸡蛋...
拿鸡蛋线程拿走了一个鸡蛋
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋
放鸡蛋线程正在放鸡蛋... 从上面的运行结果可以看出,lock同步锁做到了和synchronized同步代码块一样的功能,可以让俩个线程对同一个资源操作时达到同步的效果。但是输出结果并不是我们想要的,输出结果出现连续拿鸡蛋好几次。要想使拿鸡蛋和放鸡蛋的线程交替运行,需要线程之间的通信。 线程直线同步通信 class Plant{
//创建一个篮子
private ArrayList<String> plant = new ArrayList<>();
//创建锁
Lock lock = new ReentrantLock();
//创建Condition对象
Condition con = lock.newCondition();
//用来判断当前是否该put线程执行
boolean isPut = true;
//放鸡蛋
public void put(){
while(true){
lock.lock();
try{
while(!isPut){
con.await();//当不是put线程执行时,那么put线程就进入阻塞等待
}
System.out.println(Thread.currentThread().getName()+"正在放鸡蛋...");
plant.add("egg");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"放入了一个鸡蛋");
//put线程执行完毕,修改isPut的值为false
isPut = false;
//唤醒get的线程
con.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
//拿鸡蛋---原理同放鸡蛋
public void get(){
while(true){
lock.lock();
try{
while(isPut){
con.await();
}
if(plant.size()!=0){
System.out.println(Thread.currentThread().getName()+"正在拿鸡蛋...");
//等待1秒钟
Thread.sleep(1000);
plant.remove("egg");
System.out.println(Thread.currentThread().getName()+"拿走了一个鸡蛋");
isPut = true;
con.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}运行结果
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋
拿鸡蛋线程正在拿鸡蛋...
拿鸡蛋线程拿走了一个鸡蛋
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋
拿鸡蛋线程正在拿鸡蛋...
拿鸡蛋线程拿走了一个鸡蛋
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋
拿鸡蛋线程正在拿鸡蛋...
拿鸡蛋线程拿走了一个鸡蛋
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋 上述做到了俩个线程,一个拿鸡蛋线程,一个放鸡蛋线程的同步通信工作(使用synchronized和wait也能实现)。假设我现在有6个线程,3个放鸡蛋线程,3个拿鸡蛋线程,也要实现上述的同步通信功能又该如何实现呢? //没有修改上述代码,修改了测试类代码,直接进行测试
public class Demo2 {
public static void main(String[] args){
Plant p = new Plant();
for (int i=0;i<3;i++) {
new Thread(new Runnable() {
@Override
public void run() {
p.put();
}
},"放鸡蛋线程").start();
}
for (int i=0;i<3;i++) {
new Thread(new Runnable() {
@Override
public void run() {
p.get();
}
},"拿鸡蛋线程").start();
}
}
}但是上述代码在测试中会出现锁死现象。 因为6个线程的条件锁对象(即 Condition对象)都是相同的,所以当isPut为true是,3条拿鸡蛋的线程全部进入等待状态,3条放鸡蛋线程开始抢cpu资源当有一条线程抢到cpu资源后,其他俩条put线程释放锁。执行完程序,put线程将isPut修改为false,唤醒一条get线程,于是put线程全部进入等待状态,当前6条线程中只剩下1条get线程处于运行状态,接着这条get线程将isPut修改为true,然后唤醒一条等待条件锁对象的线程,出现锁死的现象就在这里,因为被唤醒的线程时5条线程中随机的,这里如果唤醒了一条get线程,由于isPut的值为true,那么这条线程也进入了等待状态,那么所有的线程都进入了等待状态,程序锁死。 解决方法如下 class Plant{
//创建一个篮子
private ArrayList<String> plant = new ArrayList<>();
//创建锁
Lock lock = new ReentrantLock();
Condition put = lock.newCondition();//创建放线程的条件锁对象
Condition get = lock.newCondition();//创建拿线程的条件锁对象
boolean isPut = true;
//放鸡蛋
public void put(){
while(true){
lock.lock();
try{
while(!isPut){
put.await();
}
System.out.println(Thread.currentThread().getName()+"正在放鸡蛋...");
plant.add("egg");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"放入了一个鸡蛋");
isPut = false;
get.signal();//唤醒一条拿线程
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
//拿鸡蛋
public void get(){
while(true){
lock.lock();
try{
while(isPut){
get.await();
}
if(plant.size()!=0){
System.out.println(Thread.currentThread().getName()+"正在拿鸡蛋...");
//等待1秒钟
Thread.sleep(1000);
plant.remove("egg");
System.out.println(Thread.currentThread().getName()+"拿走了一个鸡蛋");
isPut = true;
put.signal();//唤醒一条放线程
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
}运行结果
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋
拿鸡蛋线程正在拿鸡蛋...
拿鸡蛋线程拿走了一个鸡蛋
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋
拿鸡蛋线程正在拿鸡蛋...
拿鸡蛋线程拿走了一个鸡蛋
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋
拿鸡蛋线程正在拿鸡蛋...
拿鸡蛋线程拿走了一个鸡蛋
放鸡蛋线程正在放鸡蛋...
放鸡蛋线程放入了一个鸡蛋
|