2010-10-03 47 views
1

我读到以下类不是线程安全的,因为线程可能读取不一致的数据,因为线程有机会读取real的缩放版本和imaginary的未缩放版本。但我不知道如何。Java:使用同步方法的类中的线程安全

我的印象是,如果一个线程获取的锁,并在scale()方法,没有其他的线程可以在同一时间在getReal()getImaginary()方法,使其他线程不能读“半比例”的复数。这不正确吗?

class Complex 
    { 
     double real; 
     double imaginary; 

     synchronized void scale(double scaleFactor) 
     { 
      real = real * scaleFactor; 
      imaginary = imaginary * scaleFactor; 
     } 

     synchronized double getReal() 
     { 
       return real; 
     } 

     synchronized double getImaginary() 
     { 
       return imaginary; 
     } 
    } 

回答

5

考虑以下情形:

  1. 线程A调用getReal()
  2. 线程B调用scale()
  3. 线程A调用getImaginary()

这样线程A确实能得到不一致的实部和虚值。

的解决方案将是要么

  • 创建一个公共同步getter方法立刻返回两者值,或
  • 使类不可变的,如费雯丽建议。
2

没有其他线程可以在getReal()或getImaginary()在同一时间的方法,以便其他线程不能读取“一半缩放”复数。这不正确吗?

是的,这是正确的,但是......

道格拉斯指出,需要访问的实部和虚部必须执行两个单独的呼叫任何客户端:一个real()和一个imaginary() (其中另一个线程可以在其间调用scale)。您没有数据竞赛,但行为可能仍然取决于计划。

此外,您需要使字段专用,否则,其他子类或同一包中的类可能会看到“半更新”的复数。

4

不是一个真正的直接答案,但在你的情况下,最好的选择是让你的类不可变。初始化后,Complex的每个实例都不能更改。

在这种情况下,您的比例方法将使用新值创建并返回一个新的Complex对象。

请注意,这是所有JVM Number类型的工作原理。

+1

为了防止您的答案显而易见,我会补充说,同步关键字应该被删除,因为不可变性不需要锁。 – alpian 2010-10-03 21:08:09

2

如果您想要对两部分进行计算,您班级的任何客户都必须拨打getReal()然后getImaginary()

这些调用可以围绕来自另一个线程的对scale()的调用。

也许最好的解决方案是使Complex类不可变,否则你最终不得不使其他位复杂化,以锁定对象的锁定&。

相关问题