同步块如何工作?

时间:2014-03-27 18:45:51

标签: java multithreading synchronization

错误代码

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插件。

线程安全报告

  1. 计划程序类中的混合同步

    错误描述

    为了避免数据集,重要的是并发访问的字段始终由公共锁保护。如果每次访问字段时都没有保持相同的锁定对象,则可能会发生datarace。

  2. 固定代码

    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 但是未发布。我期待代码最终会死锁。但是,这不是为什么?

2 个答案:

答案 0 :(得分:3)

Java Language Specification

  

线程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;拥有"它。有意义吗?