【问题标题】:My concurrent code is synchronised but it is not printing values in sync however each thread prints the same value我的并发代码是同步的,但它没有同步打印值,但是每个线程都打印相同的值
【发布时间】:2021-04-06 13:17:10
【问题描述】:

我正在尝试演示如何使用 synchronized 关键字修复 RaceCondition。下面的代码由一个 ZooStock 对象的变量组成,该变量由 4 个线程递增和打印。我已经同步了方法(addGrass())但是所有线程打印的值都是相同的,即

当前输出: 1002g, 1002g, 1002g, 1002g

预期输出: 1001g, 1002g, 1003g, 1004g

  public static void main(String[] args){
          ZooStockSync zooStockNew = new ZooStockSync(1000, 750, 5000);
          ExecutorService executorService = null;
          try{
              executorService = Executors.newFixedThreadPool(10); //Creating a Thread pool of size 10
              for(int i=0; i<4; i++){
                  executorService.submit(()->new ZooWorkerSync(zooStockNew).addGrass()); //
              }
          }finally{
              if(executorService != null) executorService.shutdown();
          }
      }

包含同步方法的类:

class ZooWorkerSync implements Runnable {

        ZooStockSync zooStock;

        ZooWorkerSync(ZooStockSync zooStock){
            this.zooStock = zooStock;
        }

        public synchronized void addGrass(){
            zooStock.grass++;
            System.out.print(zooStock.grass + "g ");
        }
}

但是,当我在不使用 java.util.concurrent 包中的 Executor 线程的情况下以传统 (java.lang.Thread) 意义创建线程时。

public static void main(String[] args){
    ZooStockSync zooStockTraditional = new ZooStockSync(1000, 750, 5000);
    ZooWorkerSync[] workerThreads = new ZooWorkerSync[4]; //Set all elements in the array to be a ZooWorker object
    Arrays.fill(workerThreads, new ZooWorkerSync(zooStockTraditional));
    for (ZooWorkerSync workerThread : workerThreads) {
        new Thread(workerThread).start(); //Start the worker threads off (this invokes the run method in the ZooWorker class)
    }
  }

输出如预期:1001g 5010w 751h 1002g 5020w 752h 1003g 5030w 753h 1004g 5040w 754h,注意 g 是按预期升序排列的。 (忽略 h 和 w)

工作线程的run方法如下图:

@Override
public void run() {
    addGrass();
    addWater();
    addHay();
}

所以我的问题是,为什么 2 个输出不同,为什么我使用 java.util.concurent Executors 的线程打印出与传统方法相反的相同值?

【问题讨论】:

  • synchronized 在方法上意味着您正在对对象本身进行同步,并且由于您创建了 4 个不同的 ZooWorkerSync 对象,每个线程一个对象,因此同步该方法无效。
  • 你认为Arrays.fill(workerThreads, new ZooWorkerSync(zooStockTraditional)); 是做什么的?提示:不使用每个索引的新实例填充数组。
  • @akuzminykh 嘿,所以我相信 Arrays.fill() 基本上将数组的每个索引设置为使用相同的 ZooWorkerSync 实例填充,我相信我使用我创建的执行程序线程的示例一个新的实例。我不应该这样做,因为同步被用作实例级锁,因此跨多个实例同步是多余的。我需要 1 个实例和多个线程。

标签: java multithreading concurrency synchronization java.util.concurrent


【解决方案1】:

在您的 ExeutorService 示例中,您正在创建 ZooWorkerSync 类的多个实例,在 Thread 示例中,您正在重用同一个实例。

在您的第一个示例中,synchronized 关键字实际上并没有做任何事情,因为它是实例级锁。您可以尝试手动同步类。

【讨论】:

    【解决方案2】:

    synchronized 锁定一个对象,并且因为您正在同步多个对象,所以它无法按您的意愿工作。

    相反,您应该在一个通用对象上进行同步,例如类。

    class ZooWorkerSync implements Runnable {
    
            ZooStockSync zooStock;
    
            ZooWorkerSync(ZooStockSync zooStock){
                this.zooStock = zooStock;
            }
    
            public void addGrass(){
                synchronized (ZooWorkerSync.class) {
                  zooStock.grass++;
                  System.out.print(zooStock.grass + "g ");
                }
            }
    }
    

    【讨论】:

      【解决方案3】:

      我没有看到 ZooStockSync 的代码,但看起来您正在同步线程 (ZooWorkerSync) 上的方法,而不是正在共享的对象。然后访问 ZooStockSync 中的一个字段:

       public synchronized void addGrass(){
              zooStock.grass++;
              System.out.print(zooStock.grass + "g ");
          }
      

      但是对该字段(草)的访问可能不是线程安全的。每个 ZooWorkerSync 线程都可以同时访问该字段。我建议将同步放在 ZooStockSync 中增加字段的方法上。例如:

      public synchronized void incrementGrass() {
           grass++;
      }
      

      您也可以为草地使用 volatile 关键字,或将其设为AtomicInteger

      【讨论】:

        【解决方案4】:

        错误源于我在 Executors 示例中创建了几个 ZooWorkerSync 实例,上面演示的同步一词用作实例级锁,因此它在多个实例之间实际上是冗余的,它只对单个实例,因此我需要修改我的 executorService 以仅从单个实例调用 addGrass():

          public static void main(String[] args){
                  ZooStockSync zooStockNew = new ZooStockSync(1000, 750, 5000);
                  ExecutorService executorService = null;
                  try{
                      executorService = Executors.newFixedThreadPool(10); //Creating a Thread pool of size 10
                      ZooWorkerSync zooWorkerSync = new ZooWorkerSync(zooStockNew);
                      for(int i=0; i<4; i++){
                          executorService.submit(zooWorkerSync::addGrass);
                      }
                  }finally{
                      if(executorService != null) executorService.shutdown();
                  }
          }      
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多