使用synchronized锁时候,在线程运行期间,可以使用java.lang.Object上的方法实现等待通知模式,这些方法包括wait()、wait(long timeout)、notify()以及notifyAll()。
而使用Lock接口,同样有类似的方法,这些方法提供在Condition接口中。包括await、signal()、signalAll等方法。获取一个Condition必须通过Lock的newCondition()方法。
Object的wait() notify()方法使用回顾
Java.lang.Object 里的三个方法包括wait() notify() notifyAll()。
wait()
导致当前线程等待,直到其他线程调用同步监视器的notify方法或notifyAll方法来唤醒该线程。
wait(mills)
都是等待指定时间后自动苏醒,调用wait方法的当前线程会释放该同步监视器的锁定,可以不用notify或notifyAll方法把它唤醒。
notify()
唤醒在同步监视器上等待的单个线程,如果所有线程都在同步监视器上等待,则会选择唤醒其中一个线程,选择是任意性的,只有当前线程放弃对该同步监视器的锁定后,也就是使用wait方法后,才可以执行被唤醒的线程。
notifyAll()
唤醒在同步监视器上等待的所有的线程。只用当前线程放弃对该同步监视器的锁定后,也就是使用wait方法后,才可以执行被唤醒的线程。
下面是一个简单的例子,通过例子认识如何使用Object的wait、notify、notifyAll方法。
<code>public class ObjectWaitNotify implements Runnable{
public static void main(String[] args) throws InterruptedException {
ObjectWaitNotify ow = new ObjectWaitNotify();
Thread t1 = new Thread(ow);
Thread t2 = new Thread(ow);
Thread t3 = new Thread(ow);
t1.start();
t2.start();
t3.start();
Thread.sleep(1000);
synchronized(ow){
//ow.notify();//每次唤醒一个线程
ow.notifyAll();//唤醒全部等待线程
}
t1.join();
t2.join();
}
@Override
public synchronized void run() {
System.out.println(Thread.currentThread().getName()+"开始执行了");
try {
//调用wait方法等待
this.wait();
System.out.println(Thread.currentThread().getName()+"被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束执行了");
}
}/<code>
Condition和ConditionObject
Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到
Condition对象关联的锁。Condition对象是由Lock对象的newCondition()方法创建出来的,换句话说,Condition是依赖Lock对象的。
ConditionObject是AQS中的内部类,提供了条件锁的同步实现,实现了Condition接口,并且实现了其中的await(),signal(),signalALL()等方法。
在一个AQS同步器中,可以定义多个Condition,只需要多次lock.newCondition(),每次都会返回一个新的ConditionObject对象。
在ConditionObject中,通过一个等待队列来维护条线等待的线程。所以在一个同步器中可以有多个等待队列,他们等待的条件是不一样的。
等待队列是一个FIFO的队列,在队列的每个节点都包含了一个线程引用。该线程就是在Condition对象上等待的线程。这里的节点和AQS中的同步队列中的节点一样,使用的都是AbstractQueuedSynchronizer.Node类。每个调用了condition.await()的线程都会进入到等待队列中去。
在Condition中包含了firstWaiter和lastWaiter,每次加入到等待队列中的线程都会加入到等待队列的尾部,来构成一个FIFO的等待队列。
理解了基本原理之后,我们再通过简单的例子了解下Condition是如何使用的:
<code>public class ConditionUseCase implements Runnable{
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void conditionWait() throws InterruptedException {
lock.lock();
try {
condition.await();
} finally {
lock.unlock();
}
}
public void conditionSignal() throws InterruptedException {
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ConditionUseCase cus = new ConditionUseCase();
new Thread(cus).start();
new Thread(cus).start();
Thread.sleep(1000);
//通知
cus.conditionSignal();
}
@Override
public void run() {
//等待
try {
System.out.println(Thread.currentThread().getName()+"我要等待了。");
conditionWait();//等待
System.out.println(Thread.currentThread().getName()+"等待了结束,重新启程。");
} catch (InterruptedException e) {
e.printStackTrace();
}/<code>
}
}
閱讀更多 IT技術研習社 的文章