我的并发代码是同步的,但它不是同步打印值,而是每个线程打印相同的值
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();
}
}
我正在尝试演示如何使用 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();
}
}