2011-12-05 36 views
3

我正在编写一个弹簧控制器声明一些私人hashmap和更新他们在一些方法。当然,我知道并发访问的问题,所以我使用最简单的方法来避免这些问题:“同步”Java线程安全功能。但我想知道如果我应该同步我的方法或只有我需要安全地更新线程hashmap:Java线程安全,数据竞争和良好的实现

@Controller 
public class myController{ 

private HashMap<String, String> myHashMap = new HashMap<String, String>(); 

... 

//That way ? 
public synchronized updateMyHashmap(){ 
myHashMap.add(value); 
} 

//or this way ? 
public static void updateMyHashMap(){ 
synchronized(myHashMap){ 
myHashMap.add(value) 
} 
} 

} 

这些方法是等价的吗?我会有同样的应用程序行为吗?

+4

你不是只使用ConcurrentHashMap而是任何理由? –

+0

@Jon:参见对Jilles的回应 – Zamboo

回答

2

如果myHashMap变量是私有的,只能通过您的myController方法访问,那么您只需要同步这些方法。换句话说,如果你同步你的方法,它们将是线程安全的,但如果你直接访问你的myHashMap变量而不使用同步块/方法,那么你可以破坏锁。

说得更简单一些。如果你的房间有一扇门,一次只允许一个人入住,那么你一次只能在房间里有一个人......但如果你在房间里放了一个窗户,那么你仍然可以跳进通过窗户的房间。 :)

编辑:详细说明。通过将方法上的同步作为关键字,将为整个方法创建关键部分,这意味着从该方法调用结束时,其他任何内容都无法在该对象中运行。通过使用关闭块,您可以根据需要跳入和跳出关键部分。假设你有一个100行的方法,对于使用共享资源的10行需要使用一个方法,如果你在方法上同步对象将被整个100行代码锁定,但是如果你只锁定了10行你需要的线条,你只有10条线上的关键部分。

这就是你所处的情况。对于你给出的例子,绝对没有区别。

+0

有一个很大的区别:它不在同一个对象上同步。这似乎没有关系,但这个例子显然不完整,所以很难说。 – Bruno

+0

同意。为了简洁地回答这个问题,答案是否定的,在第一个例子中,你锁定了myController对象,在第二个例子中你锁定了hashmap对象。 –

+1

在你的最后一段中,我认为通过“没有别的东西能够在这个对象上运行”是非常有帮助的,你的意思是“在同一个实例对象上同步的任何东西*都不能运行”。这似乎是一个普遍的初学者的错误,认为一个'synchronized'块阻止了其他任何事情的发生,而它只阻止了其他在'synchronized'块/方法中发生的动作(在同一个监视器上)。 –

3

这些方法并不等同。

​​上的方法进行同步上this,所以:

public synchronized updateMyHashmap(){ 
    myHashMap.add(value); 
} 

相当于:

public updateMyHashmap(){ 
    synchronized(this) { 
     myHashMap.add(value); 
    } 
} 

这里,thismyController实例。 (注意:通常建议使用大写字母在Java中启动类名)。

您的第二种方法不正确,因为您正在通过静态方法访问非静态成员(myHashMap),所以不应编译。

假设它不是静态的,它会在散列表上同步而不是在myController实例上同步。

想要同步的内容几乎肯定取决于您想在此同步块中执行什么操作(例如,此value来自哪里,是否必须从其他位置采取此操作,并且整个过程是否完成操作需要使用myController实例进行同步)。

我建议您阅读Java Concurrency in Practice以了解有关同步问题的更多信息。

0

我会使用ConcurrentHashMap,而不会麻烦手动同步访问。

静态的制作方法在这里无助于你;特别是因为你需要一个对象来同步,这通常是这样的。

+1

如果您声明一个静态方法为'synchronized',它会在声明该方法的类的'Class'对象上同步。所以不用担心缺少一个默认对象来进行同步,尽管你认为使这个方法成为静态的一般与线程安全问题无关。 –

+0

@Jilles:根据java文档:“但是,即使所有操作都是线程安全的,检索操作并不需要锁定,并且也不支持以阻止所有访问的方式锁定整个表” – Zamboo

+0

@Zamboo :为什么你会*想要*虽然?你试图达到什么目标需要一个防止所有访问的锁? –

0

这两个是一些不同的东西,但可以用来实现同样的目的。

看到这里,

//Here you are locking your method 
public synchronized updateMyHashmap(){ 

    System.out.println("Inside method"); 

    //some code 

    myHashMap.add(value); 
} 

现在,在这个方法

public updateMyHashmap(){ 

    //Some code here 

    System.out.println("Inside method") 

    synchronized(this) { 
     myHashMap.add(value);  
    } 
} 

所以我觉得你现在可以很容易地看到这两种策略的区别。这取决于你想要做什么,并根据你可以选择其中的任何一个。

建议您同步要锁定的对象。

+0

我想要做的只是让我的hashmap数据正确更新:我希望当hashmap更新过程开始时(可能有多个方法调用...),那么它必须达到其结尾,然后提供安全性其他线程的数据。 – Zamboo