让我们考虑遵循可变的Java类(取自Brian Goetz的“Java Concurrency in Practice”):
@ThreadSafe
public class SynchronizedInteger {
@GuardedBy("this") private int value;
public synchronized int get() { return value; }
public synchronized void set(int value) { this.value = value; }
}
假设这个类有一个由多个线程使用的实例,那么添加到类中的以下哪些方法不会破坏其线程安全性?
解决方案#1:
public boolean isPositive() { return value > 0; }
解决方案#2:
public synchronized boolean isPositive() { return value > 0; }
解决方案#3:
public boolean isPositive() { return get() > 0; }
我的理解是,为了确保可见性,我需要确保在CPU /核心缓存中正确刷新字段值,因此解决方案#2和#3就是这样去。谁能证实这一点?
第二个问题: 假设我们有一个Java Spring单例,它在私有字段中保存了一些状态。这个单例由一些请求范围的bean使用。现在,每个请求都有自己的线程(使用它自己的调用堆栈,但共享堆)。 由于单例的状态驻留在堆上,因此单例中定义的任何方法都应以某种方式同步对其私有成员(=其状态)的访问,以确保可见性,例如,使用lock,volatile,ReentrantLock等。 我能做对吗?
答案 0 :(得分:3)
"线程安全"真的是什么意思?
任何看过任何课程的程序员都会对课程的表现抱有期望。对我来说,"线程安全"意味着如果你从多个线程调用它的方法,你就不必改变你的期望。
而不是说," SynchronizedInteger
线程安全,"我宁愿明确保证是什么,在这种情况下,它们很容易总结:它就像一个volatile
。
您的解决方案#2"和你的解决方案#3"都保留了这种保证。你的解决方案#1没有。
假设我们有[共享对象]。在[共享对象]上定义的任何方法都应该以某种方式同步对它的[状态]的访问,以确保可见性[...]我能做到正确吗?
是。这是一个开始,但可能还不够。如果状态是复杂的(即,如果它取决于两个或更多变量之间的关系)那么可能是某些改变状态的操作需要几个步骤,并且可能是这样的情况,当它的一半时通过更改,状态将无效。
在这种情况下,您需要使用互斥(例如,synchronized
块)以确保除了正在进行更改的线程之外的任何其他线程都不会看到无效状态。这意味着您不仅需要在可能创建无效状态的每一段代码周围抛出互斥锁;您还必须在每个必须保护的代码周围抛出一个互斥锁,以免看到无效状态。