【问题标题】:Increment/Decrement/Print using multiple threads(Code review) [closed]使用多个线程增加/减少/打印(代码审查)[关闭]
【发布时间】:2012-04-06 16:06:55
【问题描述】:

我试图编写一个代码,其中多个线程调用共享对象上的方法来递增/递减/打印存储在其中的计数器。我还想要这些数字在 0 到 8 之间波动。即输出可能如下所示: 0123234567654566677877666655....

有人可以看看我已经完成了什么,并给我一些关于我是否走在正确轨道上的指示:

我的共享对象:

public class SyncObj{
        private int i;
        public synchronized void inc(){
                if(i<8)
                  i++;
        }
        public synchronized void dec(){
                if(i > 0)
                   i--;
        }
        public synchronized void print(){
                System.out.print(i);
        }
}

为了防止打印上的饥饿并确保打印每个 inc/dec,我可以有一个名为 hasPrinted 的私有变量并按如下方式重写该类:

public class SyncObj{
            private int i;
            //Changed Boolean to boolean as Keith Randall pointed out
            private boolean hasPrinted = false;
            public synchronized void inc(){
                    if(i<8 && hasPrinted){
                      i++;
                      hasPrinted = false;
                    }
            }
            public synchronized void dec(){
                    if(i > 0 && hasPrinted){
                       i--;
                       hasPrinted = false;
                    }
            }
            public synchronized void print(){
                    System.out.print(i);
                    hasPrinted = true;
            }
    }

有人可以回顾一下上面的 sn-p 并检查它的陷阱和陷阱吗?

谢谢

【问题讨论】:

标签: java multithreading concurrency synchronized


【解决方案1】:

您应该习惯于使用队列进行打印。

public class SyncObj {
  private volatile int i;
  private BlockingQueue<Integer> q = new LinkedBlockingQueue<Integer>();
  public synchronized void inc() {
    if (i < 8) {
      i++;
      q.add(i);
    }
  }
  public synchronized void dec() {
    if (i > 0) {
      i--;
      q.add(i);
    }
  }
  public void print() {
    for (Integer i = q.poll(); i != null; i = q.poll()) {
      System.out.print(i);
    }
  }
  private static volatile boolean stop = false;
  public static void main(String[] args) throws InterruptedException {
    final SyncObj o = new SyncObj();

    new Thread(new Runnable() {
      @Override
      public void run() {
        while (!stop) {
          o.inc();
        }
      }
    }).start();

    new Thread(new Runnable() {
      @Override
      public void run() {
        while (!stop) {
          o.dec();
        }
      }
    }).start();

    new Thread(new Runnable() {
      @Override
      public void run() {
        while (!stop) {
          o.print();
        }
      }
    }).start();

    Thread.currentThread().sleep(1000);
    stop = true;
  }
}

我的输出如下所示:

1012345678765432101234567876543210123456787654321012345678765432101234567876543210123456787654321012345678

【讨论】:

  • 我对您的打印方法有疑问。你有理由为 (Integer i = q.poll(); i != null; i =q.poll()){ System.out.print(i);} 而不是 System.out.println( q.take())?
  • 我认为背后的想法是因为 take() 阻塞了所有其他操作,而 poll() 是非阻塞的?
  • @sc_ray 如果我能够做到while ( Integer i = q.poll() != null ) 我会做到的。另一种选择可能是q.drainTo(out),但也不允许。我真正想做的就是尽可能多地打印当时可用的。
【解决方案2】:

Boolean -> boolean,用对象代替原始类型没有意义。

您的第一个代码很好。您的第二个代码不能解决您防止饥饿或确保打印每个 inc/dec 的要求。为什么不让 inc/dec 打印值本身?

【讨论】:

  • 谢谢。我试图将打印与 dec/inc 操作分离。您能否详细说明为什么第二个代码不能确定为什么每个 inc/dec 都会被打印?同步的 inc/dec 是否会将 hasPrinted 翻转为 false 以防止任何未来的 incs/decs 直到打印在打印值后将开关翻转回 true?
  • 当然最后的 inc/dec 可能不会被打印出来。但是从调用者的角度来看,很多 inc/dec 调用可能不会被打印出来,因为它们甚至从未发生过!这是一个糟糕的 API。
  • 我确实看到了 inc 和 dec 操作是如何通过打印操作被打印操作抑制的。理想情况下,打印件应与您所说的 incs/decs 一起使用。但是假设指令是将打印操作与其他操作解耦,有没有办法在不暂停 inc 或 dec 的情况下构造打印?
  • 我不这么认为。您可以使用信号量强制 inc/dec 等待打印,但如果您希望 inc/dec 永不暂停,那么您就不走运了。
猜你喜欢
  • 1970-01-01
  • 2013-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多