如果我有一个类,称之为 X 并且 X 包含一个集合(假设我没有使用同步的集合之一,只是一个普通的集合)。
如果我要编写自己的方法 synchronized add() - 锁定是如何工作的?锁定是在 X 的实例上完成的,而不是在集合对象上完成的吗?
所以同步我的 add() 方法不会阻止 X 的许多实例调用 add() 并插入到集合中 - 因此我仍然可能遇到线程问题?
如果我有一个类,称之为 X 并且 X 包含一个集合(假设我没有使用同步的集合之一,只是一个普通的集合)。
如果我要编写自己的方法 synchronized add() - 锁定是如何工作的?锁定是在 X 的实例上完成的,而不是在集合对象上完成的吗?
所以同步我的 add() 方法不会阻止 X 的许多实例调用 add() 并插入到集合中 - 因此我仍然可能遇到线程问题?
同步方法锁定对象。如果你X.add
是同步的,它将阻止同一X
对象的其他同步方法的并发执行。如果该X
对象之外的任何人有权访问同一集合,则该集合将不受保护。
如果您希望您的收藏受到保护,请确保世界其他地方无法通过X
. 另外,这在您的问题中有点不清楚,但请注意同步的非静态方法会锁定object。假设每个X
实例都有自己的集合,它们不会相互干扰。
另一种选择,顺便说一句,是锁定集合而不是X
对象:
void add(Object o) {
synchronized(myCollection) {
myCollection.add(o);
}
}
这将同步对锁定集合而不是X
对象的访问。使用任何你觉得更容易和更有效的方法。
如果我要编写自己的方法 synchronized add() - 锁定是如何工作的?锁定是在 X 的实例上完成的,而不是在集合对象上完成的吗?
锁定是在您同步的对象上完成的——而不是对象内的任何字段。为了使锁定起作用,所有线程必须在同一个确切对象上同步。通常private final
,最好锁定一个对象。
private final Collection<...> myCollection = ...
...
synchronize (myCollection) {
myCollection.add(...);
}
虽然一种常见的模式是锁定您要保护的对象,但它实际上可以是任何常量对象。你也可以这样做:
private final Object lockObject = new Object();
...
synchronize (lockObject) {
myCollection.add(...);
}
所以同步我的 add() 方法不会阻止 X 的许多实例调用 add() 并插入到集合中 - 因此我仍然可能遇到线程问题?
如果您的应用程序的其他部分正在访问myCollection
而不在synchronized (myCollection)
块内,那么是的,您将遇到线程问题。您需要同步所有访问以正确保护集合并提供内存屏障。这意味着add(...)
, contains(...)
, 迭代器等。
通常,如果您试图保护集合或其他类,将其包装在执行同步的类中是有意义的。这隐藏了锁定并保护集合免受缺少synchronized
块的代码的意外修改。
在您的示例中,synchronized
将确保一次只有一个线程可以在该类的一个实例上调用该方法。其他方法可以访问该集合,这是不安全的。查找并发集合以获取有关线程安全集合实现的更多信息。
您是否在许多 X 实例中共享一个集合?然后,您需要在集合实例本身上进行同步。不要创建方法本身,而是将其所有代码包装在一个块中。synchronized
synchronized(coll) { ... }
另一方面,如果每个 X 都有自己的集合,那么synchronized add()
您只需要。这将保证没有两个线程同时add
在同一个实例上执行。