同步块是否阻止其他线程访问对象?

6
如果我在同步块内对列表执行某些操作,是否会阻止其他线程在其他地方访问该列表?
List<String> myList = new ArrayList<String>();

synchronized {
  mylist.add("Hello");
}

这是否会防止其他线程迭代 myList 并删除/添加值?

我希望能够向列表中添加/删除值,但同时保护它不受其他线程/方法迭代(因为列表中的值可能无效)。


在你的例子中,同步块将无法编译,你想要同步什么? - Buhake Sindi
4个回答

15
不是的。
`Synchronized` 块仅防止其他线程进入该块(更准确地说,它防止其他线程进入所有使用相同对象实例进行同步的块 - 在这种情况下,是在 `this` 上同步的块)。
您需要在 `synchronized` 块中使用要保护的实例。
synchronized(myList) {
  mylist.add("Hello");
}

这整个领域在Java教程中都有很好的解释:

http://download.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html


也许超出了这个问题和答案的范围,但如果您想更精细地控制同步,也可以使用Lock。 - Patrick
哦,没关系。我看到你链接的教程也提到了锁的概念。 - Patrick
更清晰的说法是“防止其他线程进入任何在同一实例上同步的块。” - radiantRazor

3
是的,但是前提是所有对myList的访问都受到同一个对象上同步块的保护。您发布的代码示例缺少一个您同步的对象(即获取互斥锁的对象)。如果您在不同的对象上同步或在某个实例中未同步,则其他线程可能会同时访问列表。因此,在访问列表之前,您必须确保所有线程都必须进入同一个对象上的同步块(例如,始终使用synchronized (myList) { ... }),才能访问该列表。实际上,已经有一个工厂方法可以为您包装列表的每个方法并创建同步方法:Collections.synchronizedList
然而,您可以使用Collections.synchronizedList来包装列表,以便其所有方法都是单独同步的,但这并不一定意味着您的应用程序不变量得到了维护。单独标记列表的每个方法为同步将确保列表的内部状态保持一致,但是您的应用程序可能希望获得更多,这种情况下,您需要编写一些更复杂的同步逻辑或查看是否可以利用并发API(强烈建议)。

1

这里的同步确保每次只有一个线程将“Hello”添加到myList中...

更具体地说,关于对象同步,您可以使用

synchronized( myList ) //object name
{
    //other code
}

vinod


当然,基于这一点,同步块是唯一一个可以向列表中添加内容的地方。如果其他线程在其他方法中没有使用相同的对象进行同步,则仍然可以添加和编辑myList。 - Patrick
是的,Patrick,为了照顾其他线程,我们可能需要使用Collections.synchronizedList... - Rajan

0

从我有限的Java并发控制理解来看,我认为上面的代码不太可能呈现您要寻找的行为。

同步块将使用您调用该代码的任何对象的锁,这在任何情况下都不会阻止任何其他代码访问该列表,除非该其他代码也使用相同的锁对象进行同步。

我不知道这是否有效,或者是否建议,但我认为:

List myList = new ArrayList();

synchronized(myList) { mylist.add("Hello"); }

以上代码将在列表对象的锁上同步,从而实现您所描述的行为。

然而,Java文档推荐使用以下方式来获取同步列表:

List list = Collections.synchronizedList(new ArrayList(...));

请参见: http://download.oracle.com/javase/1.4.2/docs/api/java/util/ArrayList.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接