Java:使用同步方法在类中进行线程安全

时间:2010-10-03 20:51:09

标签: java

我读到以下类不是线程安全的,因为线程可以读取不一致的数据,因为线程有可能读取real的缩放版本和imaginary的未缩放版本。但我不明白怎么做。

我的印象是,如果一个线程获得一个锁并且在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;
         }
   }

4 个答案:

答案 0 :(得分:5)

考虑以下情况:

  1. 主题A调用getReal()
  2. 主题B调用scale()
  3. 主题A调用getImaginary()
  4. 这样,线程A确实可以得到不一致的实数和虚数值。

    解决方案是

    • 创建一个通用的同步getter方法,一次返回两个值,或
    • 使这个类不可变,正如Vivien所说。

答案 1 :(得分:4)

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

在这种情况下,scale方法会创建并返回带有新值的新Complex对象。

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

答案 2 :(得分:2)

  

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

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

道格拉斯指出,任何需要访问实部和虚部的客户都必须执行两个单独的调用:一个到real(),一个到imaginary()(另一个线程可以调用{ {1}}之间)。您没有数据争用,但行为可能仍取决于计划。

此外,您需要使字段成为私有,其他一个子类或同一个包中的类可以看到“半更新”的复数。

答案 3 :(得分:2)

如果他们想要对这两个部分进行计算,那么班级的任何客户都必须致电getReal()然后getImaginary()

这些来电可以围绕从另一个线程调用scale()

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