目录
前言
在21-F.1:ReentrantLock的简单使用介绍了ReentrantLock的简单使用,可以简单的认为ReentrantLock是功能更加全面的synchronized的API形式。那么ReentrantLock的阻塞(synchronized的wait)和唤醒(synchronized的notify)如何实现呢,此时就需要和ReentrantLock一样在JDK 1.5引入的Condition出马了,相较于Object的wait(),notify()方法,Condition具有更全面的功能。
可以通过ReentrantLock.newCondition()来获得一个Condition实例,Condition与ReentrantLock绑定,持有绑定ReentrantLock的线程才能通过Condition进行阻塞和唤醒。不同于synchronized使用lockObj的wait(),notify()方法仅有一个等待队列,一个ReentrantLock可以绑定有多个Condition,即可以设置多个等待队列。一个Condition代表一个通信通道,但是要注意每个Condition的等待指令只能由同一个Condition唤醒。不同Condition之间是无法交叉唤醒的(见代码ConditionDemo)。
Condition的方法介绍
- await():阻塞等待,等效于wait()
- await(long time, TimeUnit unit):阻塞等待指定时间,指定时间后自动唤醒开始竞争锁
- awaitNanos(long nanosTimeout): 纳秒级别的阻塞等待指定时间,指定时间后自动唤醒开始竞争锁
- awaitUntil(Date deadline):阻塞等待到某一日期,到达deadline时自动唤醒开始竞争锁
- awaitUninterruptibly():无中断的等待,在这个等待状态下,此线程会无视对他的中断指令
- signal():唤醒一个阻塞的线程,等效于notify()。
- signalAll():唤醒所有阻塞的线程,等效于notifyAll()
注意Condition也是有wait()和notify()方法的,这是因为wait()和notify()是属于Object的,而任何类都继承了Object。但是wait()和notify()并不属于Condition提供的功能,将Condition作为一个普通的锁对象使用synchronized时他们才有用。因此切记不要用await()等待了却想用notify()唤醒,是唤醒不了的,他们本身就不是一套。await和signal结合使用,wait和notify结合使用。
示例代码
public class ConditionDemo {
public static void main(String[] args) {
final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
/*---------------多个线程之间的相互唤醒------------------*/
/**
for (int i = 0; i < 5; i++) {
final int j = i;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
System.out.println(j + "拿到锁");
System.out.println(j + "开始等待");
c.await();
System.out.println(j + "收到信号");
System.out.println(j + "发出信号");
c.signal();
} catch (Exception e) {
System.out.println("2error");
e.printStackTrace();
} finally {
lock.unlock(); // 一定记得要解锁!!!
}
}
});
t.start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.lock();
System.out.println("主线程拿到锁");
System.out.println("主线程发出信号");
// 唤醒是signal不是notify,notify是使用关键字synchronized时的唤醒方法!!!
// 使用notify会抛java.lang.IllegalMonitorStateException异常
c.signal(); // 唤醒,默认优先唤醒先等待的线程
// c.signalAll(); // 唤醒全部
System.out.println("主线程释放锁");
lock.unlock();
**/
/*---------------多个线程之间的相互唤醒------------------*/
/*---------------几种等待机制------------------*/
/**
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
System.out.println("1拿到锁");
System.out.println("1等待开始");
// c.await(); // 等待
// c.await(1, TimeUnit.SECONDS); //等待1s, 1s后解除等待开始竞争资源
// c.awaitNanos(1000L);// 等待1000纳秒
// c.awaitUntil(new Date(new Date().getTime() + 1000)); // 等待到某一日期
c.awaitUninterruptibly(); // 无中断的等待,在这个等待状态下,此线程会无视对他的中断指令
System.out.println("1被唤醒");
} catch (Exception e) {
System.out.println("1error");
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
t1.start();
try {
Thread.sleep(1000); // 等待1s确保t1进入等待状态,避免主线程先抢到资源执行完毕后t1得到资源才开始等待
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.interrupt(); // awaitUninterruptibly时这个语句被无视,其它等待下会抛异常java.lang.InterruptedException
lock.lock();
System.out.println("主线程拿到锁");
System.out.println("主线程发出信号");
c.signal(); // 唤醒
lock.unlock(); // 释放锁
**/
/*---------------几种等待机制------------------*/
/*-----线程3用以验证睡眠是否释放锁及验证Condition之间无法相互唤醒-----*/
/**
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
System.out.println("3拿到锁");
System.out.println("3开始等待");
c.await();
System.out.println("3收到信号");
} catch (Exception e) {
System.out.println("3error");
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
*/
/*-----线程3用以验证睡眠是否释放锁及验证Condition之间无法相互唤醒-----*/
/*--------线程持有锁并睡眠时间是不会释放资源(锁)的------------*/
/**
lock.lock();
t3.start();
System.out.println("主线程拿到锁");
System.out.println("主线程开始睡眠");
try {
Thread.sleep(1000); // 线程持有锁并睡眠时间是不会释放资源(锁)的
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程睡眠结束");
lock.unlock();
*/
/*--------线程持有锁并睡眠时间是不会释放资源(锁)的------------*/
/*---------------不同Condition之间无法相互唤醒------------------*/
/**
final Condition c2 = lock.newCondition();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
// c.signalAll();
c2.signalAll(); // c2并不能唤醒c的等待线程
lock.unlock();
*/
/*---------------不同Condition之间无法相互唤醒------------------*/
}
}
关联导航
【JAVA核心知识】系列导航 [持续更新中…]
关联导航:21-F.1:ReentrantLock的简单使用
关联导航:22:从源码看ReentrantLock的Condition
欢迎关注…