我是否正确实现了Java的同步构造?

时间:2012-11-13 03:41:35

标签: java synchronize

我对如何在Java中实现synchronized块感到困惑。

以下是一个示例情况:

public class SlotWheel extends Thread implements ActionListener
{
  private int currentTick; // This instance variable is modified in two places

  private synchronized void resetCurrentTicks()
  {
    currentTick = 0;
  }

  private synchronized void incrementCurrentTicks()
  {
    ++currentTick;
  }

  public void actionPerformed(ActionEvent e)
  {
    resetCurrentTicks();
  }
}

当程序运行时,用户可能会点击调用actionPerformed然后调用resetCurrentTicks的按钮。同时,正在运行的线程在每次循环迭代时调用incrementCurrentTicks

因为我还不熟悉Java和编程,所以我不确定我的实现是否会保护currentTick不被破坏。

我有这种感觉,我的实现只有在incrementCurrentTicks和正在运行的线程中调用actionPerformed时才有效,但是因为我正在用不同的方法操纵currentTick,我的实施是错误的。

4 个答案:

答案 0 :(得分:0)

看起来不错。

请参阅http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

同一个对象上的两个同步方法调用不可能交错

当然你应该考虑是否是GUI线程试图弄乱滴答。在您的简单情况下,它可能没问题,但在更复杂的情况下,您可能希望将“工作”推出GUI线程。

答案 1 :(得分:0)

你的直觉是正确的。跨多个方法一致地同步对类属性的访问很困难。我建议你看看java.util.concurrent.atomic.AtomicInteger,而不是试图这样做。它为您提供了对底层属性的安全,并发访问,而无需编写和测试很多样板代码。

将其合并到您的代码中,最终会得到类似的结果:

public class SlotWheel extends Thread implements ActionListener {
  private AtomicInteger currentTick = new AtomicInteger();

  private void resetCurrentTicks() {
    currentTick.set(0);
  }

  private void incrementCurrentTicks() {
    currentTick.incrementAndGet();
  }

  public void actionPerformed(ActionEvent e)
  {
    resetCurrentTicks();
  }
}

答案 2 :(得分:0)

首先,Java保证“标量”值 - 整数,字符,浮点数等 - 本身就是原子的,因为你不能同时修改这样的值并得到两个源的混合。您可以保证获得一个值或两个“同时”修改中的另一个值。但是,您可以从例如x++获得不一致的结果,因为两个线程可能会尝试同时递增x,并且可能只会发生一个增量。 (OTOH,同时执行x = 7;的两个线程显然不会相互干扰 - 同时访问不会导致爆炸或任何事情。)

接下来,要了解synchronized关键字在两个略有不同的上下文中使用 - 作为方法修饰符和块修饰符。两者之间有一些适度的差异。

当用作块修饰符时,您说synchronized(object_ref) {some block}。在这种情况下,synchronized语句会锁定object_ref标识的对象以及可能同时尝试执行引用同一对象的所有其他syncronized语句在当前声明结束时阻止它。

当你将它用作方法修饰符时,该函数是相同的,除了对于非静态方法,“this”对象是被锁定的对象,并且整个方法被“受保护”锁。

(另一方面,对于静态方法,Class对象被锁定 - 这是一个稍微特殊的情况,相当于synchronized(ClassName.class){some block}作为同步块。)

重要的是要理解,要防止两个synchronized块或方法同时执行,它们必须作为同步对象引用相同的对象,而不仅仅是其中一个同一个班级。

答案 3 :(得分:-1)

你是对的,因为它不安全。但是,您可以简单地在范围内的任何对象上进行同步,并从方法定义中删除“synchronized”

public class MyThread {
  private Object lock = new Object();
  private int counter;

  protected void threadMetod() {
    synchronized (lock) {
      counter++;
    }
  }

  public void otherReset() {
    synchronized (lock) {
      counter = 0;
    }
  }
}