假设我有一个方法,可以通过两个或多个线程访问,我想让它保持线程安全。
public int getVal(int x, int y, MyClass myObj)
{
int z;
z = getInt(myObj);
return x + y + z;
}
在这里,我认为我们不必同步x + y,因为它们是原语。
我们假设getInt(myObj)
修改myObj的状态并影响z的值。
因此,我必须为行z = getInt(myObj);
提供同步,但仅当两个线程都在'myObj'引用中传递相同的实例时。作为API的编码器,我不知道两个线程是否都会为'myObj'传递相同的实例。在某些情况下,这些线程可能会在“myObj”引用中传递相同的MyClass实例,而在其他情况下,它们可能会在“myObj”引用中传递不同的MyClass实例。
那么,如何确保线z = getInt(myObj)
的线程安全性? (当然,我们不希望在传递的实例不同时进行同步,并且只有在传递的实例相同时才需要同步。我很清楚这是无法确定的)。
假设MyClass不能成为不可变的,我认为以下可能是一个解决方案。
synchronized(myObj)
{
z = getInt(myObj);
}
这是一个正确的解决方案吗?并且,我们还可以通过其他方式确保
的线程安全性z = getInt(myObj); (but only in case of different instances)?
答案 0 :(得分:3)
你拥有的是正确的。当你在一个对象上synchronize
时,它锁定该实例而不是该类。因此,如果我将对象的相同的 *实例*传递给两个不同的方法,它将正确锁定该对象。但是,如果我传递两个不同的实例,则不会有任何锁定,因为两个实例都有自己的 lock 。
答案 1 :(得分:2)
如果getInt
未修改this
的状态,则该方法是线程安全的。 myObj对象的线程安全性是其类的负责任:MyClass,或者包含它的对象。并非所有可能将其作为论据的方法的责任,恕我直言。
您的解决方案(synchronized(myObj)
)是正确的:如果在两个线程中使用相同的myObj,则两个线程将无法同时执行getInt
方法。如果两个myObj不同,它们将同时执行。
答案 2 :(得分:1)
synchronized(myObj) { z = getInt(myObj); }
将按照您的意图行事,但在Paremeter上进行同步会产生许多其他问题。例如,某些其他线程可能已经在该对象上进行同步(例如,该对象可能有一个正在调用它的同步方法)并且您可能会遇到死锁情况。
同步应该像其他任何东西一样封装。最好的解决方案是将getInt
方法添加到MyClass
并在该方法中的某个私有成员上进行同步。通过这种方式,没有其他人可以解决您用于实现sycnrhonization的问题。
E.g:
public class MyClass {
private Object lockObject = new Object();
public int getInt() {
synchronized(lockObject) {
// do your thing
}
}
}
请参阅:Avoid synchronized(this) in Java? 参考封装同步的重要性。
答案 3 :(得分:0)
要回答“并且,我们可以通过哪些方式确保线程安全......但仅限于不同实例”,同步整个方法或创建另一个公共对象以充当所有线程的锁定同步它而不是myObj。
答案 4 :(得分:0)
如果对myObject
方法的唯一更改来自此getInt
方法,那么您的同步就足够了。如果还有其他修改器,请确保它们在同一对象上同步。
答案 5 :(得分:0)
我不同意所有“你的同步是正确的”答案。如果您的用户有2个线程,并且其中一个线程已经锁定了该对象,该怎么办?随之而来的是死锁。
此外,x + y + z不是原子的。在CPU级别,它将变为
int temp = x + y;
int res = temp + z;
我会告诉你更多:long1 + long2在32位机器上不是原子的。
我认为您唯一的选择是同步整个方法。