/**
* 说明: 其中一个任务产生偶数,而其他任务消费这些数字。
* 这里,消费者任务的唯一工作就是检查偶数的有效性。
*
* @create @author Henry @date 2016-11-24
*
*/
/**
* 首先,我们定义EvenChecker,即消费者任务,因为它将在随后所有的
* 示例中被复用。为了将EvenChecker与我们要试验的各种类型的生成器
* 解耦,我们将创建一个名为IntGenerator的抽象类,它包含EvenChecker
* 必须了解的必不可少的方法:即一个Next()方法,和一个可以执行撤销的方法。
* 这个类没有实现Generator接口,因为它必须产生一个int,而泛型不支持基本类型的参数
*
* @create @author Henry @date 2016-11-24
*
*/
public abstract class IntGenerator {
private volatile boolean canceled = false;
public abstract int next();
public void cancel() {
canceled = true;
}
public boolean isCanceled() {
return canceled;
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 在本例中可以被撤销的类不是Runnable,而所有依赖于IntGenerator对象的EvenChecker任务
* 将测试它,以查看它是否已经被撤销,正如你在run()中所见。通过这种方式,共享公共资源(IntGenerator)
* 的任务可以观察该资源的终止信号。这可以消除所谓竞争条件,即两个或更多的任务竞争响应某个条件,
* 因此产生冲突或不一致结果的情况。
* 你必须仔细考虑并防范并发系统失败的所有可能途径,
* 例如,一个任务不能依赖于另一个任务,因为任务关闭的顺序无法得到保证。
* 这里通过使任务依赖于非任务对象,我们可以消除潜在的竞争条件。
*
* @create @author Henry @date 2016-11-24
*
*/
public class EvenChecker implements Runnable{
private IntGenerator generator;
private final int id;
public EvenChecker(IntGenerator generator, int id) {
this.generator = generator;
this.id = id;
}
/**
* EvenChecker任务总是读取和测试从与其相关的IntGenerator返回的值。注意,
* 如果generator.isCanceled()为true,则run()将返回,这将告知EvenChecker.test()
* 中的Executor该任务完成了。任何EvenChecker任务都可以在与其相关联的IntGenerator
* 上调用cancel(),这将导致所有其他使用该IntGenerator的EvenChecker得体地关闭。
* 在后面个节中,你将看到Java包含的用于线程终止的各种更通用的机制。
*
* @create @author Henry @date 2016-11-24
*/
@Override
public void run() {
while(!generator.isCanceled()){
int val=generator.next();
if(val%2!=0){
System.out.println(val+" not even!");
generator.cancel();//Cancels all EvenCheckers
}
}
}
/**
* test() 方法通过启动大量使用相同的IntGenerator的EvenChecker,设置并执行对任何类型的
* IntGenerator的测试。如果IntGenerator引发失败,那么test()将报告它并返回,
* 否则,你必须按下Control-C来终止它。
*
* @create @author Henry @date 2016-11-24
* @param gp
* @param count
*/
public static void test(IntGenerator gp,int count){
System.out.println("Press Control-C to exit");
ExecutorService exec=Executors.newCachedThreadPool();
for (int i = 0; i < count; i++)
exec.execute(new EvenChecker(gp, i));
exec.shutdown();
}
public static void test(IntGenerator gp){
test(gp, 10);
}
}
/**
* 一个任务有可能在另一个任务执行第一个对currentEvenValue的递增操作之后,
* 但是没有执行第二个操作之前,调用next()方法(即,代码中被注释为
* "Danger point here!"的地方)。这将使这个值处于"不恰当"的状态。为了证明这是可能发生的,
* EvenChecker.test()创建了一组EvenChecker对象,以连续地读取并输出同一个
* EvenGenerator,并测试检查每个数值是否都是偶数。如果不是,就会报告错误,而程序也将关闭。
*
*
* @create @author Henry @date 2016-11-24
*
*/
public class EventGenerator extends IntGenerator{
private int currentEvenValue=0;
/**
* 如果你正在写一个变量,它可能接下来将被另一个线程读取,
* 或者正在读取一个上次已经被另一个线程写过的变量,那么你必须使用同步,并且,
* 读取线程都必须使用相同的监视器锁同步。
*
* @create @author Henry @date 2016-11-24
*/
@Override
public int next() {//synchronized
++currentEvenValue;//Danger point here!
//Thread.yield();
++currentEvenValue;
return currentEvenValue;
}
/**
* 这个程序最终将失败,因为各个EvenChecker任务在EvenGenerator处于"不恰当的"状态时,
* 仍能够访问其中的信息。但是,更加你使用的特定操作系统和其他实现细节,直到
* EvenCenerator完成多次循环之前,这个问题都不会被探测到。如果你希望更快地发现
* 失败,可以尝试着将对yield()的调用放置在第一个和第二个递增操作之间。这只是并发程序的部分问题
* 如果失败的概率非常低,那么即使存在缺陷,它们也可能看起来是正确的 。
*
* 有一点很重要,那就是要注意到递增程序自身也需要多个步骤,并且在递增过程中任务
* 可能会被线程机制挂起---也就是说,在Java中,递增不是原子性的操作。因为,如果
* 不保护任务,即使单一的递增也是不安全的。
*
* @create @author Henry @date 2016-11-24
* @param args
*/
public static void main(String[] args) {
EvenChecker.test(new EventGenerator());
}
}
用lock解决问题
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* MutexEvenGenerator添加了一个被互斥调用的锁,并使用lock()和unlock()方法
* 在next()内部创建了临界资源。当你在使用Lock对象时,将这里所示的惯用法内部化是很重要的:
* 紧接着对lock()的调用,你必须放置在finally子句中带有unlock()的try-finally语句中。
* 注意,return语句必须在try子句中出现,以确保unlock()不会过早发生,从而将数据暴露给了
* 第二个任务。
*
* @create @author Henry @date 2016-11-24
*
*/
public class MutexEvenGenerator extends IntGenerator {
private int currentEvenValue = 0;
private Lock lock=new ReentrantLock();
/**
* 尽管try-finally所需的代码比synchronized关键字要多,但是
* 这也代表了显示的Lock对象的优点之一。如果使用synchronized关键字时,
* 某些事物失败了,那么就会抛出一个异常。但是你没有机会去做任何清理工作,
* 以维护系统使其处于良好状态。有了显式的Lock对象,你就可以使用finally
* 子句将系统维护在正确的状态了。
*
* 大体上,当你使用sunchronized关键字时,需要写的代码量更少,并且用户错误出现
* 的可能性也会降低,因为通常只有在解决特殊问题时,才使用显式的Lock对象。
* 例如,用synchronized关键字不能尝试着获取锁且最终获取锁会失败,或者尝试着
* 获取锁一段时间,然后放弃它,要实现这些,你必须使用concurrent类库。
*
*
* @create @author Henry @date 2016-11-24
*/
@Override
public int next() {
lock.lock();
try{
++currentEvenValue;//Danger point here!
Thread.yield();
++currentEvenValue;
return currentEvenValue;
}finally{
lock.unlock();
}
}
public static void main(String[] args) {
EvenChecker.test(new MutexEvenGenerator());
}
}
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLock 允许你尝试着获取单最终未获取锁,这样如果其他人已经获取了这个锁,那你
* 就可以决定离开去执行其他一些事情,而不是等待直到这个锁被释放,就像在untimed()方法中
* 所看到的。在timed()中,作出了尝试去尝试获取锁,该尝试可以在两秒之后失败(注意,使用
* 了java se5 的TimeUnit类来指定时间单位)。在main中,作为匿名类而创建了一个单位的Thread,
* 它将获取锁,这使得untimed()和timed()方法对某些事物将产生竞争。
*
* 显示的lock对象在枷锁和释放锁方面,相对于内建的synchronized锁来说,还赋予了你更细粒度的
* 控制力。这对于实现专有同步结构是很有用的,例如用于遍历链接列表中的节点,节节传递的枷锁机制
* (也称为锁耦合),这种遍历代码必须在释放当前节点的锁之前不活下一个节点锁。
*
* @create @author Henry @date 2016-11-28
*
*/
public class AttemptLocking {
private ReentrantLock lock = new ReentrantLock();
public void untimed() {
boolean captured = lock.tryLock();
try {
System.out.println("tryLock():" + captured);
} finally {
if (captured)
lock.unlock();
System.out.println("untimed over");
}
}
public void timed() {
boolean captured = false;
try {
captured = lock.tryLock(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
System.out.println("tryLock(2, TimeUnit.SECONDS):" + captured);
} finally {
if (captured)
lock.unlock();
System.out.println("timed over");
}
}
public static void main(String[] args) {
final AttemptLocking al = new AttemptLocking();
al.untimed();// True --lock is available
al.timed();// True -- lock is available
// Now create a separate task to grab the lock;
new Thread() {
{
//setDaemon(true);
}
public void run(){
al.lock.lock();
System.out.println("acquired");
}
}.start();
Thread.yield(); //Give the 2nd tesk a chance
al.untimed(); //False --lock grabbed by task
al.timed(); //False --Lock grabbed by task
}
}