如何正确创建SynchronizedStack类?

时间:2011-02-02 16:09:04

标签: java multithreading synchronization

我在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。做这样的事情的正确方法是什么?

  • 还有其他意见/建议吗?

谢谢!

4 个答案:

答案 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}}行为封装在另一个类中。