当一个线程修改了一个对象的值,另外一个线程需要感知到这个变化,并且做出相应的操作时,可以使用Java中的等待/通知机制去实现这个功能。
与等待/通知相关的方法是Java中所有对象都具备的方法,它们被定义在java.lang.Object中。
notify()方法:通知一个在对象上等待的线程,使其从wait()方法中返回,并且返回的前提是该线程获取到了对象的锁。
notifyAll()方法:与notify()不同的是,会通知所有等待在该对象上的线程。
wait()方法:调用该方法的线程会在对象上等待,进入WAITING状态,只有等待另外的线程通知或者被中断才会返回。调用wait()方法后,会释放对象的锁。
wait(long timeout):超时等待一段时间,如果等待指定时间后还没有收到通知就会超时返回,参数单位为毫秒。
wait(long timeout, int nanos):超时等待更加精确的实现方法,可精确到纳秒。
当线程A调用的对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A将会收到通知从O的wait()返回,继续执行后续操作。
等待/通知的经典实现方式:
等待方(消费者):
synchronized(obj){
while(判断条件不满足){
obj.wait();
}
其它处理逻辑
}
通知方(生产者):
synchronized(obj){
改变条件
obj.notifyAll();
}
使用等待/超时机制实现一个简单的对象池,模拟多个线程不断地从对象池中取出资源,使用完后归还资源到对象池中,使得其它线程可以继续使用这些资源。
定义资源类:
public class Resource {
private String name;
public Resource(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
定义对象池:
public class ResourcePool {
// 使用双向列表保存所有的资源
private LinkedList pool = new LinkedList();
public ResourcePool(int poolSize) {
// 初始化pool,将可用的对象放入pool中
for (int i = 0; i
pool.add(new Resource("RES_" + i));
}
}
// 获取资源
public Resource getResource(long timeout) {
long future = System.currentTimeMillis() + timeout;
long waitRemain = timeout;
synchronized (pool) {
// 判断有没有资源,若没有则等待
while (pool.isEmpty()) {
try {
// 在pool对象上等待,此时会释放pool对象的锁
pool.wait(waitRemain);
} catch (InterruptedException e) {
e.printStackTrace();
}
waitRemain = future - System.currentTimeMillis();
if (waitRemain <= 0) {
// 若等待超时,则返回null
System.out.println(Thread.currentThread().getName() + " get resource time out");
return null;
}
}
// 将资源从pool中移除,并返回资源
Resource resource = pool.removeFirst();
System.out.println(Thread.currentThread().getName() + " get resource: " + resource.getName());
return resource;
}
}
// 释放资源
public void releaseResource(Resource resource) {
if (resource != null) {
synchronized (pool) {
// 归还资源
pool.addLast(resource);
System.out.println(Thread.currentThread().getName() + " released resource: " + resource.getName());
// 通知所有等待在pool对象上的线程
pool.notifyAll();
}
}
}
}
main方法:
public static void main(String[] args) {
// 多个线程从资源池中同步获取资源
final ResourcePool resPool = new ResourcePool(5);
// 使用闭锁,让所有线程同时开始执行
final CountDownLatch startGate = new CountDownLatch(1);
for (int i = 0; i
Thread thread = new Thread(new Runnable() {
public void run() {
try {
// 等待闭锁的计数器为0时,才能继续向下执行
startGate.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取资源,5秒超时
Resource resource = resPool.getResource(5000);
if (resource != null) {
try {
// 随机等待(1~10秒)若干秒后,再释放资源
Random random = new Random();
Thread.sleep((random.nextInt(9) + 1) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
resPool.releaseResource(resource);
}
}
});
thread.setName("Th_" + i);
thread.start();
}
// 闭锁的计数减1,此时值为0,所有线程开始继续执行
startGate.countDown();
}
控制台输出:
Th_9 get resource: RES_0
Th_7 get resource: RES_1
Th_8 get resource: RES_2
Th_6 get resource: RES_3
Th_3 get resource: RES_4
Th_6 released resource: RES_3
Th_0 get resource: RES_3
Th_7 released resource: RES_1
Th_4 get resource: RES_1
Th_8 released resource: RES_2
Th_2 get resource: RES_2
Th_1 get resource time out
Th_5 get resource time out
Th_0 released resource: RES_3
Th_4 released resource: RES_1
Th_9 released resource: RES_0
Th_2 released resource: RES_2
Th_3 released resource: RES_4