用dw做网站怎么添加音乐/seo搜索引擎推广
明日复明日,明日何其多?我生待明日,万事成蹉跎。———《明日歌》
这句诗给人的启示是:世界上的许多东西都能尽力争取和失而复得,只有时间难以挽留。人的生命只有一次,时间永不回头。反复告诫人们要珍惜时间,今日的事情今日做,不要拖到明天,不要蹉跎岁月。
上一篇遗留的问题: 有没有某种方法可以达到,当条件为真时,线程立即醒过来执行呢?
答案是肯定的。
所以,来看第三种方法,代码清单如下:
public class BoundedBuffer<V> extends BaseBoundedBuffer{protected BoundedBuffer(int capacity) {super(capacity);// TODO Auto-generated constructor stub}public synchronized void put(V v) throws InterruptedException {while(isFull())wait();doPut(v);notifyAll();}public synchronized V take() throws InterruptedException {while(isEmpty())wait();V v = (V) doTake();notifyAll();return v;}
}
这里采用了Object的wait,notify,notifyAll方法,实现我们想要的效果。Object.wait会自动释放锁,并请求操作系统挂起当前线程,从而使得其他线程能够获得这个锁并修改对象的状态。当被挂起的线程醒来时,它将在返回之前重新获取锁。
到这里,BoundedBuffer 已经变得足够好,简单易用。
每一次wait调用之前都必须持有锁,并且这个锁必须保护着构成条件谓词的状态变量。
使用 wait 常见的几个问题说明。
过早唤醒
wait 方法的返回并不一定意味着条件就为真了。比如下面几个原因
- 因为可能在发出通知的线程调用 notifyAll 时,条件谓词可能已经变成真,但在重新获取锁时再次变为假了。在线程被唤醒到wait重新获取锁的这段时间里,可能有其他线程已经获取了这个锁,并修改了对象的状态。
- 条件谓词从调用wait起,根本就没有变成真。因为你并不知道另一个线程为什么调用 notify 和 notifyAll,也许可能是另一个条件谓词变为真了。
基于上面两个原因,每当线程从 wait 中唤醒时,都必须再次测试条件谓词。如果不为真,就继续等待。由于可能每次醒来条件谓词都不为真,所以必须在一个循环中调用 wait,每次循环都要判断条件谓词。
void stateDependentMethod() throws InterruptedException {synchronized(lock) {while(!conditionPredicate()) {//此处必须是循环lock.wait();}}
}
丢失的信号
丢失的信号是指:线程必须等待一个已经为真的条件,但在开始等待之前没有检查条件谓词。现在,线程将等待一个已经发过通知的事件。导致一直处于等待状态。
通知 notify 和 notifyAll
两者的区别是,notify 只能唤醒一个线程,也就是说在众多等待的线程中随机挑选一个唤醒。notifyAll是唤醒所有的等待线程。假如多个线程基于不同的条件谓词在同一个条件队列上等待,使用notify将是危险的,因为它只唤醒一个并且不保证唤醒的就是与之相对应的条件谓词。
所以优先使用 notifyAll 而不是 notify。 然而,这样还是有可能导致性能上的问题。
假如有 10 个线程在等待,那么调用 notifyAll 将唤醒所有线程,并使得他们在锁上发生竞争。然后大多数又回到睡眠状态。因此,在每个线程执行一个事件的同时,将出现大量的上下文切换操作以及发生竞争的锁获取操作。
上面说的第三种方式,称为使用内置条件队列,因为它使用的是内置锁 synchronized。它存在一些缺陷,每个内置锁都只能有一个相关联的条件队列,而像BoundedBuffer这种类中,多个线程可能在同一个条件队列上等待不同的条件谓词,这使得 notifyAll 唤醒的不是同一类型的条件谓词。
如果想要更加细分,唤醒不同的条件谓词,可以使用 Lock 和 Condition,代替内置锁,它是一种更加灵活的选择。
Condition 接口如下:
public interface Condition {void await() throws InterruptedException;boolean await(long time, TimeUnit unit) throws InterruptedException;long awaitNanos(long nanosTimeout) throws InterruptedException;void awaitUninterrupteibly();boolean awaitUnit(Date deadline) throws InterruptedException;void signal();void signalAll();
}
对于每个 Lock, 可以有任意数量的 Condition 对象。在Condition对象中,与wait,notify,notifyAll方法对应的是 await,signal,signalAll.
所以,实现缓存有界队列的第四种方法是使用两个 Condition, 分别为 notFull 和 notEmpty。如下代码所示:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ConditionBoundedBuffer<T> {protected final Lock lock = new ReentrantLock();private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();private final T[] items = (T[]) new Object[10];private int tail, head, count;public void put(T t) throws InterruptedException{lock.lock();try {while(count == items.length)notFull.await();items[tail] = t;if(++tail == items.length)tail = 0;++count;notEmpty.signal();}finally {lock.unlock();}}public T take() throws InterruptedException {lock.lock();try {while(count == 0)notEmpty.await();T t = items[head];if(++head == items.length)head = 0;--count;notFull.signal();return t;}finally {lock.unlock();}}
}
没错,这就是最终版本了,类库中的 ArrayBlockingQueue 就是这样实现的。ConditionBoundedBuffer 的行为和 BoundedBuffer 相同,但它对条件队列的使用方式更加容易理解。多个Condition使得我们分析代码时更加清晰,并且使用signal极大的减少在每次缓存操作中发生的上下文切换和锁请求的次数。
与内置锁和条件队列一样,当使用显示的 Lock 和 Condition 时,也必须满足锁,条件谓词和条件变量之间的三元关系。在条件谓词中包含的变量必须由 Lock 来保护,并且在检查条件谓词以及调用 await 和 signal 时,必须持有 Lock 对象。
恩,本文完结!