2011-03-09 61 views
5

假设我有一个方法,它可以被两个或多个线程访问,并且我想让它成为线程安全的。同步java中的形式参数

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

您的同步块已足够。每当同步对象的监视器被另一个线程占用时,JVM就会阻止该调用。 – 2011-03-09 19:25:38

+0

@Brent,有没有其他方法可以做到这一点?就是想! – 2011-03-09 19:33:12

+0

您的synchronized()是危险的。在下面检查我的答案。 – iluxa 2011-03-09 20:11:42

回答

0

要回答“并在哪些方面我们可以保证线程安全的......但只有在不同情况下的情况下”,同步整个方法或创建另一个公共对象,以充当所有线程的锁并在其上同步而不是myObj。

3

你有什么是正确的。当你在一个对象上创建它时,它的锁定不在那个类上。因此,如果我将相同的 *实例*的对象传递给两个不同的方法,它将正确锁定该对象。不过,如果我通过两个不同的实例,将不会有任何锁定,因为两个实例各自拥有

+3

更好的方法是同步MyClass上的getInt方法。 – 2011-03-09 19:36:28

+0

他有什么是正确的,但不好的做法。通过锁定myObj,他可能会向客户端代码引入陷阱,并希望在该对象上进行同步。 http://stackoverflow.com/questions/442564/avoid-synchronizedthis-in-java/442601#442601 – 2011-03-09 20:49:22

+0

是的,但如果他委托工作getInt()并使用内部对象来做锁定,我认为他会组。但是,我同意这有一个陷阱。 – 2011-03-09 20:52:41

2

如果getInt不修改this的状态,则该方法是线程安全的。 myObj对象的线程安全性是其类的响应性:MyClass或持有它的对象。恕我直言,不是所有可能将其作为论点的方法的责任。

但是,您的解决方案(synchronized(myObj))是正确的:如果在两个线程中都使用相同的myObj,则两个线程将不能同时执行getInt方法。如果两个myObjs不同,它们将同时执行。

+0

如果某人同时调用'setInt'会怎么样?如果别人可能写作,你仍然可能会遇到问题。 – 2011-03-09 20:06:54

+0

这不是一个getter(访问器),我会假定它从输入中提取或派生某些东西,所以它不太可能存在setInt。 – Robin 2011-03-09 21:57:57

+0

“myObj对象的线程安全性是其类的责任”:Java库的本质是线程安全常见吗?或者,像集合库一样,是否假设用户将在适当的位置添加锁定? – Karmastan 2011-03-10 02:45:05

0

如果有问题的myObject唯一的变化来自这getInt方法,那么你的同步就足够了。如果还有其他修改器,请确保它们在同一个对象上同步。

0

我不同意所有“你的同步是正确的”答案。如果你的用户有2个线程,并且其中一个线程已经在对象上持有一个锁,该怎么办?僵局随之而来。

另外,x + y + z不是原子的。在CPU的水平,这将成为

int temp = x + y; 
int res = temp + z; 

,我会告诉你更多:long1 + long2是不能在32位机器原子。

我认为你唯一的选择是同步整个方法。

+1

在他的方法中添加基元很好,因为基元是通过值传递的。一个方法的原始形式参数不能在线程间共享,因为它们是按值传递的。 – 2011-03-09 20:37:28

1
synchronized(myObj) { z = getInt(myObj); } 

会做你想要的,但在参数表上同步会产生许多其他问题。例如,某个其他线程可能已经在该对象上同步(例如,可能该对象具有正在对其调用的同步方法),并且可能陷入死锁情况。

同步应该像其他任何东西一样被封装。最好的解决方案是将getInt方法添加到MyClass,并在该方法中的某个私有成员上进行同步。通过这种方式,没有其他人可以利用你正在使用的来实现你的syngrhonization。

例如为:

public class MyClass { 
    private Object lockObject = new Object(); 
    public int getInt() { 
    synchronized(lockObject) { 
     // do your thing 
    } 
    } 
} 

见这样的:在参考Avoid synchronized(this) in Java? 到封装的同步的重要性。