【问题标题】:Is it necessary to use Java synchronization on a field that might be modified by a single thread scheduler?是否有必要在可能被单线程调度程序修改的字段上使用 Java 同步?
【发布时间】:2019-04-24 19:05:01
【问题描述】:

我有一个类(外部类),具有以下内容

  1. 一个包含数据的列表字段

  2. List 字段的 getter 方法

  3. 从在线数据库返回列表的 getDataFromDB 方法

  4. 实现 Runnable 并在其 run 方法中修改 List 字段的内部类

  5. 在外部类的initialize方法中,会设置一个SingleThreadScheduledExecutor,调度内部类运行

我想知道是否有必要在哪里放置一个同步块

  1. 在 getter 方法中读取 List 字段 (2)

  2. List 字段在内部类的 run 方法中被修改(这是一个 Runnable,将由 SingleThreadScheduledExecutor 调度)(4)

public class OutterClass{

  private List<Data> data;
  private ScheduledExecutorService scheduledExecutorService;

  @PostConstruct
  private void initialize(){
    scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
    scheduledExecutorService.scheduleWithFixedDelay(new InnerClass(), // other configs);
  }

  private List<Data> getDataFromOnlineDB() {/* some work */}

  public synchronized List<Data> getData() { return data; }

  private class InnerClass implements Runnable {
    @Override
    public void run() {
      synchronized (OutterClass.this) {
        data = getDataFromOnlineDB();
      }
    }
  }
}

【问题讨论】:

    标签: java concurrency synchronization


    【解决方案1】:

    要回答您的问题,我们需要了解托管数据的预期访问模式。

    如所写,您的代码处理一种访问模式,其中托管数据在对象构建后的某个时间点被分配,托管数据可以在对象构建后的任何时间点被访问,其中分配发生在一个线程中,访问发生在其他线程中。这是您打算支持的访问模式吗?

    这是一个有助于清理代码的小更新。唯一的区别是句法重排。功能完全一样。

    需要注意的主要更新是对“数据”的赋值是在 setter 中进行的。这使得同步更加清晰。

    public class OuterClass {
    
      // Some data to be managed.  Centralize all explicit references.
      private List<Data> data;
      public synchronized List<Data> getData() { return data; }
      public synchronized void setData(List<Data> data) { this.data = data; }
    
      // An executor used to launch code which asynchronously
      // compute and assigns the managed data.
      private ScheduledExecutorService scheduledExecutorService;
    
      // Tie into this instance lifecycle by launching
      // a thread that computes and assigns managed data.
      @PostConstruct
      private void initialize(){
        scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleWithFixedDelay(new InnerClass(), // other configs);
      }
    
      // Code which computes the data ...
      private List<Data> getDataFromOnlineDB() {/* some work */}
    
      // A runnable used to asynchronously compute and assign the managed data.
      private class InnerClass implements Runnable {
        @Override
        public void run() {
            setData( getDataFromOnlineDB() );
        }
      }
    }
    

    【讨论】:

    • 使getData()方法同步不会使列表同步,也许应该使用synchronizedList来代替?
    • 这达到了同步的目的:是看守读取和写入“数据”(忽略“数据”是一个列表),还是看守获取和放置数据为一个列表?编写的代码似乎将“数据”作为一个简单的变量进行操作,在这种情况下同步就足够了。如果检索“数据”的代码同时更新列表,则需要同步集合。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多