我在Java中创建了一个简单的同步Stack对象,仅用于培训目的。 这是我做的:
public class SynchronizedStack {
private ArrayDeque<Integer> stack;
public SynchronizedStack(){
this.stack = new ArrayDeque<Integer>();
}
public synchronized Integer pop(){
return this.stack.pop();
}
public synchronized int forcePop(){
while(isEmpty()){
System.out.println(" Stack is empty");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return this.stack.pop();
}
public synchronized void push(int i){
this.stack.push(i);
notifyAll();
}
public boolean isEmpty(){
return this.stack.isEmpty();
}
public synchronized void pushAll(int[] d){
for(int i = 0; i < d.length; i++){
this.stack.push(i);
}
notifyAll();
}
public synchronized String toString(){
String s = "[";
Iterator<Integer> it = this.stack.iterator();
while(it.hasNext()){
s += it.next() + ", ";
}
s += "]";
return s;
}
}
以下是我的问题:
可以不同步isEmtpy()
方法吗?我认为这是因为即使另一个Thread同时修改堆栈,它仍然会返回一致的结果(没有进入isEmpty状态的操作既不是初始状态也不是最终状态)。或者同步对象的所有方法是否同步是一种更好的设计?
我不喜欢forcePop()
方法。我只是想创建一个线程,它能够在弹出元素之前等到项目被推入堆栈,我认为最好的选择是使用wait()
中的run()
进行循环线程的方法,但我不能因为它抛出IllegalMonitorStatException
。做这样的事情的正确方法是什么?
还有其他意见/建议吗?
谢谢!
答案 0 :(得分:9)
Stack
本身已经同步,因此再次应用同步没有意义(如果需要非同步堆栈实现,请使用ArrayDeque
)
不行(除了前一点的事实),因为缺乏同步可能会导致内存可见性效果。
forcePop()
非常好。虽然它应该通过InterruptedException
而不是让它遵循可中断阻塞方法的合同。它允许您通过调用forcePop()
来中断在Thread.interrupt()
呼叫时被阻止的线程。
答案 1 :(得分:0)
未同步isEmpty()
的唯一问题是您不知道底下发生了什么。虽然你的推理是合理的,但它假定基础Stack
也以合理的方式表现。在这种情况下可能是这样,但一般情况下你不能依赖它。
问题的第二部分,阻塞弹出操作没有任何问题,请参阅this以完整实施所有可能的策略。
另外一个建议是:如果您正在创建一个可能在应用程序的几个部分(甚至多个应用程序)中重用的类,请不要使用synchronized
方法。这样做:
public class Whatever {
private Object lock = new Object();
public void doSomething() {
synchronized( lock ) {
...
}
}
}
原因是您并不真正知道您的班级用户是否想要在Whatever
个实例上进行同步。如果他们这样做,他们可能会干扰班级本身的运作。这样你就拥有了自己的私人锁,没有人可以干涉。
答案 2 :(得分:0)
假设stack.isEmpty()
不需要同步可能是真的,但您依赖的是您无法控制的类的实现细节。
Stack的javadoc声明该类不是线程安全的,因此您应该同步所有访问。
答案 3 :(得分:0)
我觉得你有点混淆习语。您使用SynchronizedStack
支持java.util.Stack
,而java.util.Vector
依次为synchronized
,即wait()
。我认为你应该将notify()
和{{1}}行为封装在另一个类中。