|
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
|---|---|
| 作者 | 正文 |
|
最后更新时间:2007-06-03 关键字: Thread
在“The Problem with Threads"论文中提到的用来抨击线程模型的实例。懒得说了,看代码直接:
java 代码
java 代码
java 代码
注意:在Java中,局部变量都是线程私有的,不用担心访问冲突,要担心的就是实例变量和类变量。 声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
最后更新时间:2007-06-03
应该是没有问题,但不是个好的实现,因为while(localSeqnum != globalNum)太抢CPU时间,在你不知道每个Listener的valueChanged要执行的时间长短时更不能使用这种死循环轮巡的策略。
另外,不管你怎么实现,“线程A和线程B依次调用setValue,然后线程B抢在线程A之前通知大家”肯定是无法避免的,synchronized只能保证互斥,它不保证调用顺序,这完全由操作系统决定。就你给的那个实现,如果线程A在17行时就被操作系统挂起了,让线程B先执行,那结果还是线程B抢在线程A前通知。 所以LZ有点过度设计了,不过如果是学习那倒不是坏事。 |
|
| 返回顶楼 | |
|
最后更新时间:2007-06-03
修改为
synchronized(this){ while(localSeqnum != globalNum){ wait(); } } 后面: synchronized(this){ globalNum++; notifyAll(); } 看这样有没有问题。 这里只是解决了 轮循的问题,不能从根本达到目标,就如楼上所说。 这样还不如简单的把整个方法sync呢。 |
|
| 返回顶楼 | |
|
最后更新时间:2007-06-03
Godlikeme 写道 这样还不如简单的把整个方法sync呢。 整个方法括起来是不行的,会有死锁的可能,JDK源码util包中Observable也只是进行block同步。 |
|
| 返回顶楼 | |
|
最后更新时间:2007-06-03
也不能将整个setValue方法synchronized掉,因为如果Listener的操作时间很长的话,会阻塞其他线程对其他同步方法的调用,比如说其他线程想添加Listener什么的。所以只要改到第二个实现就够了,再改就过了。
|
|
| 返回顶楼 | |
|
最后更新时间:2007-06-03
确实,因为接口的具体实现可能会引起两阶段锁带来的死锁问题,
那就只能如lz把listner.changeValue部分放到synch外面执行,通过一些guard condition来保证执行过程。 但我觉得在这里,方法里synchronized(this) 又没有把this传给listner,应该不会产生这种问题。 哎呀,不行,如果在里面wait()就挂了。 |
|
| 返回顶楼 | |
|
最后更新时间:2007-06-03
max.h.chen 写道 应该是没有问题,但不是个好的实现,因为while(localSeqnum != globalNum)太抢CPU时间,在你不知道每个Listener的valueChanged要执行的时间长短时更不能使用这种死循环轮巡的策略。
另外,不管你怎么实现,“线程A和线程B依次调用setValue,然后线程B抢在线程A之前通知大家”肯定是无法避免的,synchronized只能保证互斥,它不保证调用顺序,这完全由操作系统决定。就你给的那个实现,如果线程A在17行时就被操作系统挂起了,让线程B先执行,那结果还是线程B抢在线程A前通知。 所以LZ有点过度设计了,不过如果是学习那倒不是坏事。 这个例子是“The Problem with Threads"论文中提到的,专门用来抨击Thread模型。当然,这个例子应该是作者为了说明Thread模型存在问题而杜撰的,在实际中很难出现:两个线程去更改同一个主题。如果这样设计,那一定是一个糟糕的设计。 这位作者就质疑了作者所举的这个Observer pattern实例,具体请看:http://chaosinmotion.com/blog/?p=26 |
|
| 返回顶楼 | |
|
最后更新时间:2007-06-03
max.h.chen 写道 也不能将整个setValue方法synchronized掉,因为如果Listener的操作时间很长的话,会阻塞其他线程对其他同步方法的调用,比如说其他线程想添加Listener什么的。所以只要改到第二个实现就够了,再改就过了。
因为是接口,并不知道实现细节,所以存在这种假设。 那就可以扩展到所有涉及接口的地方都要做这样的假设处理,这就是写多线程非常讨厌的地方。 具体实现很可能不需要这样的处理。 一般情况下,像listener中存在这种响应时间比较长的处理,应该在方法中起一个线程来做,本方法立刻返回。 举一个例子就是eclipse中的编译。 |
|
| 返回顶楼 | |
|
最后更新时间:2007-06-03
Godlikeme 写道 确实,因为接口的具体实现可能会引起两阶段锁带来的死锁问题,
那就只能如lz把listner.changeValue部分放到synch外面执行,通过一些guard condition来保证执行过程。 但我觉得在这里,方法里synchronized(this) 又没有把this传给listner,应该不会产生这种问题。 还是有可能的,比如说下面的Listener实现。
class MyListener implements Listener {
private ValueHolder valueHolder;
MyListener(ValueHolder valueHolder) {
this.valueHolder = valueHolder;
}
public synchronized void doSomething() {
synchronized(valueHolder) {
// do something here
}
}
public void valueChanged(int newValue) {
synchronized (this) {
synchronized(valueHolder) {
// do something here
}
}
}
}
如果没有上下文,光看这个MyListener是没有问题的,因为valueChanged和doSomething两个方法使用synchronized的顺序是一样的(MyListener.this -> ValueHolder.this),但是如果放在现在这个上下文里,一旦你将ValueHolder#setValue()整个synchronized掉的话,MyListener#valueChanged获得锁的顺序就变成ValueHolder.this -> MyListener.this -> ValueHolder.this,那就会可能死锁了。 |
|
| 返回顶楼 | |
|
最后更新时间:2007-06-03
甚是,甚是。
|
|
| 返回顶楼 | |






