如何正确创建SynchronizedStack类?

发布于 2024-10-15 12:06:36 字数 1681 浏览 0 评论 0原文

我用 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() 方法可以吗?我认为这是因为即使另一个线程同时修改堆栈,它仍然会返回一致的结果(没有任何操作会进入既不是初始状态也不是最终状态的 isEmpty 状态)。或者让一个同步对象的所有方法都同步是一个更好的设计吗?

  • 我不喜欢 forcePop() 方法。我只是想创建一个线程,它能够等到一个项目被推入堆栈后再弹出一个元素,我认为最好的选择是使用 wait() 中的 wait() 进行循环线程的 run() 方法,但我不能,因为它会抛出 IllegalMonitorStatException。执行此类操作的正确方法是什么?

  • 还有其他评论/建议吗?

谢谢你!

I made a simple synchronized Stack object in Java, just for training purposes.
Here is what I did:

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;
    }
}

Here are my questions:

  • Is it OK not to synchronize the isEmtpy() method? I figured it was because even if another Thread is modifying the stack at the same time, it would still return a coherent result (there is no operation that goes into a isEmpty state that is neither initial or final). Or is it a better design to have all the methods of a synchronized object synchronized?

  • I don't like the forcePop() method. I just wanted to create a thread that was able to wait until an item was pushed into the stack before poping an element, and I thought the best option was to do the loop with the wait() in the run() method of the thread, but I can't because it throws an IllegalMonitorStatException. What is the proper method to do something like this?

  • Any other comment/suggestion?

Thank you!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

無處可尋 2024-10-22 12:06:36
  • Stack 本身已经同步,因此再次应用同步是没有意义的(如果您想要非同步堆栈实现,请使用 ArrayDeque

  • 这不是好的(除了上一点的事实),因为缺乏同步可能会导致内存可见性影响。

  • forcePop() 非常好。尽管它应该传递 InterruptedException 而不捕获它,以遵循可中断阻塞方法的约定。它允许您通过调用 Thread.interrupt() 来中断在 forcePop() 调用中阻塞的线程。

  • Stack itself is already synchronized, so it doesn't make sense to apply synchronization again (use ArrayDeque if you want a non-synchronized stack implementation)

  • It's NOT OK (aside from the fact from the previous point), because lack of synchronization may cause memory visibility effects.

  • forcePop() is pretty good. Though it should pass InterruptedException without catching it to follow the contract of interruptable blocking method. It would allow you to interrupt a thread blocked at forcePop() call by calling Thread.interrupt().

郁金香雨 2024-10-22 12:06:36

假设 stack.isEmpty() 不需要同步可能是正确的,但您依赖于您无法控制的类的实现细节。
Stack 的 javadoc 声明该类不是线程安全的,因此您应该同步所有访问。

Assuming that stack.isEmpty() won't need synchronization might be true, but you are relying on an implementation detail of a class that you have no control over.
The javadocs of Stack state that the class is not thread-safe, so you should synchronize all access.

起风了 2024-10-22 12:06:36

我认为你有点混合了习语。您使用 java.util.Stack 支持您的 SynchronizedStack,而 java.util.Stack 又由 java.util.Vector 支持,即 同步。我认为您应该将 wait()notify() 行为封装在另一个类中。

I think you're mixing idioms a little. You are backing your SynchronizedStack with java.util.Stack, which in turn is backed by java.util.Vector, which is synchronized. I think you should encapsulate the wait() and notify() behaivor in another class.

断肠人 2024-10-22 12:06:36

不同步 isEmpty() 的唯一问题是您不知道下面发生了什么。虽然您的推理是合理的,但它假设底层 Stack 也以合理的方式运行。在本例中可能就是这样,但一般情况下您不能依赖它。

您问题的第二部分,阻塞弹出操作没有任何问题,请参阅 this 用于完整实施所有可能的策略。

还有一个建议:如果您要创建一个可能在应用程序的多个部分(甚至多个应用程序)中重复使用的类,请不要使用同步方法。这样做:

public class Whatever {
  private Object lock = new Object();

  public void doSomething() {
    synchronized( lock ) {
      ...
    }
  }
}

这样做的原因是您并不真正知道您的类的用户是否想要在您的 Whatever 实例上同步。如果他们这样做,他们可能会干扰班级本身的运作。这样您就拥有了自己的私人锁,任何人都无法干预。

The only problem with not synchronizing isEmpty() is that you don't know what's happening underneath. While your reasoning is, well, reasonable, it assumes that the underlying Stack is also behaving in a reasonable manner. Which it probably is in this case, but you can't rely on it in general.

And the second part of your question, there's nothing wrong with a blocking pop operation, see this for a complete implementation of all the possible strategies.

And one other suggestion: if you're creating a class that is likely to be re-used in several parts of an application (or even several applications), don't use synchronized methods. Do this instead:

public class Whatever {
  private Object lock = new Object();

  public void doSomething() {
    synchronized( lock ) {
      ...
    }
  }
}

The reason for this is that you don't really know if users of your class want to synchronize on your Whatever instances or not. If they do, they might interfere with the operation of the class itself. This way you've got your very own private lock which nobody can interfere with.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文