我有理解为什么使用synchronized(syncObject)
比使用synchronized(this)
更好的问题。例如,这个类:
public class Pool implements ObjectPool {
private Object[] pool;
private int initialCapacity;
private int available = 0;
private int waiting = 0;
private final Object syncObject = new Object();
public Pool(int initialCapacity) {
this.initialCapacity = initialCapacity;
pool = new Object[initialCapacity];
}
public void releaseObject(Object o) throws Exception {
synchronized (syncObject) {
pool[available] = o;
available++;
}
if (waiting > 0) {
notify();
}
}
}
答案 0 :(得分:2)
因为如果你使用this
,那么另一个尝试执行另一个方法的线程将不得不等待,而如果你使用一个对象作为锁,你只能限制这个关键部分。
答案 1 :(得分:2)
如果您使用的是synchronized(this)
,则任何引用您对象的代码都可以通过synchronized(yourObject)
与您的同步互动。这可能会产生意想不到的副作用,或者鼓励其他开发人员使用synchronized(this)
编写依赖于您的代码的代码。
使用synchronized(myLockObj)
而myLockObj
是对象中私有的对象,其他任何人都无法在同一对象上同步。因此,没有与您的锁定进行交互,也没有与类之外的代码依赖于强制执行线程安全的方式。换句话说,您可以在以后更改您的实现,而不会破坏您班级以外的其他代码。
举个例子,Hashtable
是synchronized
本身所有方法都为Hashtable
的类。以下是有效的:
Hashtable t;
…
synchronized(t) {
if(!t.containsKey(k)) t.put(k,v);
}
由于这是有保证的,因此实施永远不会改变。
相反,ConcurrentHashMap
没有提供从外部锁定地图的任何可能性。因此,您必须使用提供的putIfAbsent
方法来实现类似的功能。这允许将来对实现进行改进以提高吞吐量,事实上,已经进行了这样的改进。
答案 2 :(得分:1)
您必须了解使用同步的原因。您这样做是为了确保没有数据争用。如果对象的一个单个字段只能进行单个数据竞争,并且有几个这样的竞争,则整个对象上没有点同步,因为您减慢了执行速度。请考虑以下代码:
class SynchTestThis {
Collection col1 = new ArrayList();
Collection col2 = new ArrayList();
public void addCol1(Object obj) {
synchronized(this) {
col1.add(obj);
}
}
public void addCol2(Object obj) {
synchronized(this) {
col2.add(obj);
}
}
}
class SynchTestObj {
Collection col1 = new ArrayList();
Collection col2 = new ArrayList();
public void addCol1(Object obj) {
synchronized(col1) {
col1.add(obj);
}
}
public void addCol2(Object obj) {
synchronized(col2) {
col2.add(obj);
}
}
}
如果SynchTestThis
同时向两个集合添加元素是不可能的。如果是SynchTestObj
,则可以完成。
换句话说,选择要同步的对象是正确识别和保护关键部分的问题。
答案 3 :(得分:1)
正如其他答案所指出:通过this
同步,您公开您要锁定的对象。
但要再次指出为什么这可能是一个问题:它可能导致死锁。想象一下,例如,这两个方法由两个不同的线程执行:
private final Object localMonitor = new Object();
private final Pool pool = new Pool();
void methodA()
{
synchronized (localMonitor)
{
pool.releaseObject(null);
}
}
void methodB()
{
synchronized (pool)
{
synchronized (localMonitor)
{
System.out.println("Performing some work...");
}
}
}
第一个线程将在localMonitor
上同步,然后尝试从Pool
类调用该方法。
第二个线程将在pool
实例上同步,然后尝试在localMonitor
上进行同步。
就其本身而言,这段代码是有效的"。这实际上很好。 除非,Pool
类的方法也会同步。然后,线程将陷入死锁。使用专用的syncObject
可以避免这种情况。
再次说明,作为一个可运行的示例:只需通过切换注释行来将对象更改为synchronized
以查看差异。
class Pool
{
private final Object syncObject = new Object();
public void releaseObject(Object o)
{
//synchronized (syncObject) // <----------- This will work
synchronized (this) // // <----------- This will cause a deadlock
{
System.out.println("Modify pool");
}
}
}
class SimpleSynchronizeExample
{
public static void main(String[] args)
{
SimpleSynchronizeExample s = new SimpleSynchronizeExample();
s.start();
}
private final Object localMonitor = new Object();
private final Pool pool = new Pool();
void methodA()
{
synchronized (localMonitor)
{
try { Thread.sleep(100); } catch (Exception e) {}
pool.releaseObject(null);
}
}
void methodB()
{
synchronized (pool)
{
try { Thread.sleep(100); } catch (Exception e) {}
synchronized (localMonitor)
{
System.out.println("Performing some work...");
}
}
}
private void start()
{
Thread tA = new Thread(new Runnable()
{
@Override
public void run()
{
methodA();
}
});
Thread tB = new Thread(new Runnable()
{
@Override
public void run()
{
methodB();
}
});
tA.start();
tB.start();
try
{
tA.join();
tB.join();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("Done");
}
}