环境(Condition)因素对对象的等待和通知方法(waite(),notify()和notifyall()方法)作为不同的状态对象,给这些对象的多个等待集合的影响。这里我们用锁(Lock)取代同步(synchronize)方法和阻塞,环境(Condition)取代对象的等待(wait)和唤醒(notifycation)的方法。
Note 环境(Condition)实例本质上是包含一个锁(lock)。为了包含一个环境(Condition)实例给一个锁实例,我们运用锁(Lock)的newCondition()方法。
环境(Condition)声明了下面的方法:
- void await(): 强迫请求的线程处于等待状态直到它需要签名或打断。
- boolean await(long time, TimeUnit unit): 强迫请求的线程处于等待状态直到它需要签名或打断,或直到指定等待的时间范围内。
- long awaitNanos(long nanosTimeout): 强迫请求的线程处于等待状态直到它需要签名或打断,或直到指定等待的时间范围内。
- void awaitUniterruptibly():强迫请求的线程处于等待状态直到它需要签名。
- boolean awaitUnit(Date deadline): 强迫请求的线程处于等待状态直到它需要签名或打断,或直到指定截止时间。
- void signal():唤醒一个等待的线程。
- void signalAll():唤醒所有等待的线程。
下面的例子使用环境(Condition)的方法实例生产者与消费者的应用。
package com.owen.thread.chapter7;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PC
{
public static void main(String[] args)
{
Shared s = new Shared();
new Producer(s).start();
new Consumer(s).start();
}
}
class Shared
{
private char c;
private volatile boolean available;
private final Lock lock;
private final Condition condition;
Shared()
{
available = false;
lock = new ReentrantLock();
condition = lock.newCondition();
}
Lock getLock()
{
return lock;
}
char getSharedChar()
{
lock.lock();
try
{
while (!available)
try
{
condition.await();
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
available = false;
condition.signal();
} finally
{
lock.unlock();
return c;
}
}
void setSharedChar(char c)
{
lock.lock();
try
{
while (available)
try
{
condition.await();
} catch (InterruptedException ie)
{
ie.printStackTrace();
}
this.c = c;
available = true;
condition.signal();
} finally
{
lock.unlock();
}
}
}
class Producer extends Thread
{
private final Lock l;
private final Shared s;
Producer(Shared s)
{
this.s = s;
l = s.getLock();
}
@Override
public void run()
{
for (char ch = 'A'; ch <= 'Z'; ch++)
{
l.lock();
s.setSharedChar(ch);
System.out.println(ch + " produced by producer.");
l.unlock();
}
}
}
class Consumer extends Thread
{
private final Lock l;
private final Shared s;
Consumer(Shared s)
{
this.s = s;
l = s.getLock();
}
@Override
public void run()
{
char ch;
do
{
l.lock();
ch = s.getSharedChar();
System.out.println(ch + " consumed by consumer.");
l.unlock();
} while (ch != 'Z');
}
}
上面的代码PCmain()方法实例Shared、Producer和Consumer的类。而Shared是通过Producer和Consumer实例化的。
Producer和Consumer的构造者是在主线中被调用的。因为Share的实例化需要在Producer和Consumer的线程中,这个实例必须出现这些有效的线程中(尤其这些线程运行在不同的核中)。通过声明s使最终完成任务。在这个域中我们声明了一个volatile的变量,为了确保s在被初始化后不会更改。
观察Share的构造器,你会察觉到,它创建了一个锁为lock = new ReentrantLock(),和一个环境(Condition),通过锁来创建的condition = lock.newCondition()。这个锁通过getLock()的方法获取有效的producer和consumer.
producer线程执行Share的void setSharedChar(char c)的方法去产生一个字符和写出一条信息。这个方法锁定先前那个锁对象,并且进入一个while的循环重复测试是否可用,如果生产的字母可用于消费那么就会返回true.
当available为true时,生产者就会去执行环境(condition)的await()方法去等待available变为fale。当消费者需要消费字母时,消费者发出信号给环境(condition)去唤醒生产者。(我运用一个循环去替代if语句,因为虚假唤醒是可能的和available可能总是为true.)
之后离开循环,生产者的线程会记录新的字母,availble为true表明一个字母可以给消费者,并且通知环境去唤醒一个等待的生产者。最后,释放锁和执行setShareChar()。
Note 在Producer的run()方法中,我们锁住了setSharedChar()和System.out.println();在Consumer的run()方法中,我们锁了getSharedChar()和System.out.println()。生产信息之前需要消费信息;消费信息之前需要生产信息。
在消费者的行为线程和getSharedChar()方法,类似于我们仅仅描述的生产者和setSharedChar()的方法。
Note 我并没有使用try/finally的语句去确保锁在生产和消费中无效,因为我们不希望在上下文中抛出异常。
执行上面的代码,你可能等到如下的结果:
A produced by producer.
A consumed by consumer.
B produced by producer.
B consumed by consumer.
C produced by producer.
C consumed by consumer.
D produced by producer.
D consumed by consumer.
••••