我有一个对象,其内部可变状态由一个或多个线程不断更新。该对象是同步的,目标是定期从另一个线程中保存其状态(通过序列化):
public class Counter implements Serializable {
private int dogCount;
private int catCount;
public synchronized void updateFromDogThread( int count ) {
dogCount = count;
}
public synchronized void updateFromCatThread( int count ) {
catCount = count;
}
}
问题:
ObjectOutputStream
是否会执行序列化块,直到Counter
上没有任何线程再次运行?Counter
的同步不使用内部锁,而是使用其他锁?答案 0 :(得分:3)
每当需要修改类的序列化时,您必须实现特殊的私有方法void writeObject(ObjectOutputStream)
。 ObjectOutputStream
使用此方法而不是默认算法。
在您的情况下,您希望序列化与对象同步。所以你要做的就是在方法中添加synchronized
关键字。您仍然可以使用默认实现defaultWriteObject
:
private synchronized void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
}
答案 1 :(得分:2)
这不安全,但实现起来相对容易:
synchronized (counter) {
out.writeObject(counter);
}
正如您所注意到的,锁定的对象是任意的,因此序列化机制如何知道如何获得相关的锁定。更糟糕的是,序列化和对象图的顺序也很随意,因此任何锁定尝试都会导致死锁。即使使用上面的解决方案,您也在锁内执行复杂操作,因此请注意死锁。
答案 2 :(得分:2)
- 在这种情况下序列化安全吗?
没有。正如@Tom Hawtin所说,您需要执行自己的锁定,以确保在序列化对象时不会更改对象。
- 它是如何在引擎盖下工作的?也就是说,ObjectOutputStream是否会执行序列化块直到没有线程再次在Counter上运行?
ObjectOutputStream
没有锁定。如果有必要,由应用程序来完成。
- 如果Counter的同步不使用内部锁定,而是使用其他锁定怎么办?
然后,您的应用程序还需要使用该其他锁来锁定更新,同时进行序列化。
如果您正在序列化的状态只包含一个具有两个字段的对象的状态,那么锁争用和粒度应该不是问题。但是如果对象很复杂,那么锁争用可能会成为问题,因为获取锁的问题可能没有死锁的风险。那种情况需要仔细设计。