了解线程和并发性。请考虑以下代码:
class A {
protected final Object lock = new Object();
public void remove(Object obj){
synchronized(lock){
// remove the object
}
}
public void add(Object obj){
synchronized(lock){
// add the object
}
}
}
这个代码是线程安全的,因为当一个线程处于添加或删除过程中时,没有两个不同的线程可以add
或remove
。
现在考虑A:
的以下sublcassclass B extends A {
public void update1(Object original, Object newObj){
remove(original);
add(original);
}
public void update2(Object original, Object newObj){
synchronized(lock) {
remove(original);
add(newObj);
}
}
}
类B
必须实现线程安全的update
方法。现在,据我所知update1
不是线程安全的,因为操作不是原子的,即remove
和add
的执行之间没有同步(如果错误则纠正我)。
update2
是实现线程安全update
方法的正确方法吗?
在同一lock
上嵌套同步块是否有任何缺点?
答案 0 :(得分:7)
update2
是实现线程安全更新方法的正确方法吗?
是的,确实如此。您已达到原子性,并且与单个add
和remove
方法的来电者兼容。
在同一个锁上嵌套同步块有什么缺点吗?
不,因为锁是可重入,这意味着第二次获取不会再记住再次获取锁定,所以它不会被释放,直到执行两个发布操作。
答案 1 :(得分:0)
synchronized
的使用使整个对象“锁定”:当另一个线程运行其中一个对象同步方法时,没有其他线程可以调用任何方法。
这种行为确实会减慢一些程序。
作为替代方案,您可以使用ReentrantLock
。
以下是它如何适用于帖子中的代码:
class A {
protected final Lock lock = new ReentrantLock();
public void remove(Object obj){
lock.lock();
try {
// remove the object
} finally {
lock.unlock();
}
}
public void add(Object obj){
lock.lock();
try {
// add the object
} finally {
lock.unlock();
}
}
}
class B extends A {
// ...
public void update2(Object original, Object newObj){
lock.lock();
try {
remove(original);
add(newObj);
} finally {
lock.unlock();
}
}
}
答案 2 :(得分:0)
锁是重新进入的,除非你在每个地方看到死锁
重入锁定即使锁定已经由同一个线程获得也可以获得锁定。
如果存在必须删除并添加原子的场景,为什么不在超类方法中提供removeAndAdd。
出于安全原因,请确保您的锁定对象标记为私有和最终。