我读到以下类不是线程安全的,因为线程可以读取不一致的数据,因为线程有可能读取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;
}
}
答案 0 :(得分:5)
考虑以下情况:
getReal()
scale()
getImaginary()
这样,线程A确实可以得到不一致的实数和虚数值。
解决方案是
答案 1 :(得分:4)
不是一个直接的答案,但在你的情况下,最好的选择是使你的类不可变。初始化后,Complex
的每个实例都无法更改。
在这种情况下,scale方法会创建并返回带有新值的新Complex
对象。
请注意,这是所有JVM Number
类型的工作方式。
答案 2 :(得分:2)
同时没有其他线程可以在getReal()或getImaginary()方法中,以便其他线程无法读取“半缩放”复数。这不正确吗?
是的,这是正确的,但是......
道格拉斯指出,任何需要访问实部和虚部的客户都必须执行两个单独的调用:一个到real()
,一个到imaginary()
(另一个线程可以调用{ {1}}之间)。您没有数据争用,但行为可能仍取决于计划。
此外,您需要使字段成为私有,其他一个子类或同一个包中的类可以看到“半更新”的复数。
答案 3 :(得分:2)
如果他们想要对这两个部分进行计算,那么班级的任何客户都必须致电getReal()
然后getImaginary()
。
这些来电可以围绕从另一个线程调用scale()
。
可能最好的解决方案是使Complex
类不可变,否则你最终不得不使许多其他位变得复杂以锁定&解锁对象。