我的并发代码是同步的,但它不是同步打印值,而是每个线程打印相同的值

My concurrent code is synchronised but it is not printing values in sync however each thread prints the same value

我正在尝试演示如何使用 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 包含同步方法:

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.lang.Thread) 意义上创建线程时,没有使用 java.util.concurrent 包中的执行器线程。

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)

工作线程的运行方法如下所示:

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

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

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

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

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

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

草地也可以使用volatile关键字,或者AtomicInteger

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

相反,您应该在公共对象上同步,例如 class。

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 ");
            }
        }
}

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

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

错误是由于我在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();
          }
  }