Viscent / javamtp

《Java多线程编程实战指南(设计模式篇)》源码

Home Page:http://viscent.iteye.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

关于two-phase termination(两阶段终止)代码部分的疑问

xurenjie opened this issue · comments

文海兄:
看了这个模式的代码后有点疑问。在AlarmAgent#sendAlarm部分,首先判断了terminationToken是否被关闭,然后才将消息入队列,但是这两个操作并不是原子操作。有否可能出现这种情况:当这个判断通过后(没有关闭),在执行插入队列之前,terminationToken被置成关闭,且这个时候消费端刚好把队列里面最后一个消息消费完,在下一次循环的时候由于发现terminationToken被置成关闭且reservation为0,所以直接退出。这个时候再执行之前的插入队列操作,那这种情况下这个消息就会永远在队列里面不会被消费者消费了。

还有个疑惑,就是AbstractTerminatableThread#terminate这个方法的finally部分。在这个部分的实现中,先是判断了reservation是否小于等于0,如果是的话再interrupt,但是这个地方有否可能出现在interrupt之前有元素被放到队列中,然后由于interrupt线程后导致消费线程直接退出而被放入的元素不能被消费的情况?(在run方法中try catch是放在整个循环外面的,所以一旦interrupt的话就会导致这个消费者退出)

另外再请教个问题,就是TerminationToken的中的coordinatedThreads里面存放的是WeakReference的queue,这个地方为什么要用WeakReference?我的理解是WeakReference会随时被GC,如果被GC的话就不能通知其他线程了,这样是否有点问题?

不好意思。才看到这个issue。我subscribe了通知,但并没有收到通知。
第1个问题:
纯粹从代码本身来分析,的确存在你说的这种情况。不过第5章(Two-phase Termination模式)的这个例子着重点在Two-phase Termination这个模式本身。当然,这个模式很多情况下会与Producer-Consumer模式一起使用,所以在第7章(Producer-Consumer模式)里会讲到:在Producer-Consumer模式中终止线程时要先确保Producer线程终止,只有在Producer线程终止之后才能够去终止Consumer线程。这样就避免了上述问题。当然,Producer-Consumer模式中,一个Producer也可能是其他Producer的Consumer,所以如果存在这种情形,那么要递推地先去终止最顶层的那个(那些)Producer线程。

第2个问题:
这个问题的答案和第1个问题一样:在先确保Producer线程终止的情况下再去终止Consumer线程,那么从Consumer线程的角度来看,在terminationToken.isToShutdown为true的情况下,在reservation一旦到达0,那么reservation就恒定为0,因此这个时候去调用interrupt()是安全的。

第3个问题:
这个并无问题:设someObj为任意对象实例,设someWR=new WeakReference(someObj),那么someWR可以被GC掉的前提是JVM中不存在其他对someObj可达(Reachable)的强引用(Strong Reference)。换句话说,如果someWR可以被GC掉,那么说明someObj也可以被(或者已经被)GC掉。换到我们这个例子中,如果coordinatedThreads对线程(对象)的引用(Weak Reference)可以被GC掉,那么说明其引用的线程已经终止。既然线程已经终止,那么我们就无需再“通知”它。