0

我对如何synchronized在 Java 中实现块有点困惑。

这是一个示例情况:

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

4 回答 4

0

看起来还可以。

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

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

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

于 2012-11-13T03:47:33.627 回答
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();
  }
}
于 2012-11-13T03:51:00.970 回答
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要防止两个块或方法同时执行,它们必须引用同一个对象作为同步对象而不仅仅是同一个类中的一个。

于 2012-11-13T04:02:57.800 回答
-1

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

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

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

  public void otherReset() {
    synchronized (lock) {
      counter = 0;
    }
  }
}
于 2012-11-13T03:53:07.340 回答