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

0md85ypi  于 2021-06-27  发布在  Java
关注(0)|答案(4)|浏览(339)

我试图演示如何使用synchronized关键字修复racecondition。下面的代码由一个zoostock objects变量组成,该变量由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.lang.thread)创建线程而不使用java.util.concurrent包中的executor线程时。

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

所以我的问题是,为什么这两个输出不同,为什么我使用java.util.concurent执行器的线程打印相同的值,而不是传统的方法?
lnxxn5zx

lnxxn5zx1#

在你的 ExeutorService 例如,您正在创建 ZooWorkerSync 同学们,在教室里 Thread 示例:重复使用同一示例。
在第一个例子中 synchronized 关键字实际上没有任何作用,因为它是示例级锁。您可以尝试在类上手动同步。

u5i3ibmn

u5i3ibmn2#

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

nle07wnf3#

错误源于我在executors示例中创建了几个zoowerSync示例,上面演示的synchronized这个词用作示例级锁,因此在多个示例中实际上是冗余的,它只对单个示例有用,因此,我需要修改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();
          }
  }
70gysomp

70gysomp4#

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

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

但是进入那块地(草地)可能不是线程安全的。每个zooworkersync线程可以同时访问该字段。我建议在zoostocksync中使用synchronized方法来增加字段。例如:

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

也可以对grass字段使用volatile关键字,或者将其设置为atomicinteger。

相关问题