错误代码
public class Scheduler {
private Object lock = new Object();
private int interval;
private int period;
public Scheduler(int interval , int period) {
this.interval = interval;
this.period = period;
}
public synchronized void setInterval(int interval) {
this.interval = interval;
}
public synchronized void setPeriod(int period) {
this.period = period;
}
public void updateScheduler(int interval , int period) {
synchronized(lock) {
setPeriod(period);
setInterval(interval);
}
}
}
我的代码中有一些线程安全问题可以找到它们,我在项目中运行了线程安全的eclipse插件。
线程安全报告
计划程序类中的混合同步
错误描述
为了避免数据集,重要的是并发访问的字段始终由公共锁保护。如果每次访问字段时都没有保持相同的锁定对象,则可能会发生datarace。
固定代码
public class Scheduler {
private Object lock = new Object();
private int interval;
private int period;
public Scheduler(int interval , int period) {
this.interval = interval;
this.period = period;
}
public void setInterval(int interval) {
synchronized(lock) {
this.interval = interval;
}
}
public void setPeriod(int period) {
synchronized(lock) {
this.period = period;
}
}
public void updateScheduler(int interval , int period) {
synchronized(lock) {
setPeriod(period);
setInterval(interval);
}
}
}
我测试了这段代码,它运行正常。我的问题是,我从 updateScheduler 中调用 setPeriod 和 setInteval 。当 updateScheduler 被调用时,对象锁定已经被锁定,那么我正在调用 setInterval 和 updateScheduler 但是锁未发布。我期待代码最终会死锁。但是,这不是为什么?
答案 0 :(得分:3)
线程t可能会多次锁定特定监视器;每次解锁都会逆转一次锁定操作的效果。
因此,您可以为同一个对象嵌套synchronized
块。
synchronized(lock) {
synchronized(lock) {
synchronized(lock) {
// ad nauseam and within method calls
}
}
}
这在Intrisic Locks and Synchronization
的Java教程中进一步解释回想一下,线程无法获取另一个线程拥有的锁。 但是线程可以获得它已经拥有的锁。允许线程多次获取同一个锁可以重入 同步。这描述了同步代码的情况, 直接或间接调用也包含的方法 同步代码,两组代码使用相同的锁。没有 可重入同步,同步代码将需要很多 另外的预防措施,以避免线程导致自己阻止。
答案 1 :(得分:3)
只有一个显示器被锁定在固定代码中(lock
),所以死锁是不可能的。
要修复原始代码,您还可以同步updateScheduler
方法,而不是使用lock
对象。它会实现同样的目标。
就你的问题而言,Java锁定是" 可重入",这意味着,当一个"锁定"一段代码试图锁定"它再次(即使用相同的监视器),它理解它的同一个监视器,因此不会阻止/等待它被解锁。它知道它已经拥有了#34;拥有"它。有意义吗?