【问题标题】:volatile variable is not giving expected outputvolatile 变量没有给出预期的输出
【发布时间】:2019-07-16 04:23:33
【问题描述】:

我读到 volatile 变量副本将由所有线程共享,一旦执行完成,更新值将由每个线程获取,但是在下面使用线程池的程序中没有给出我期望的输出,任何一个都可以告诉我原因?

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Task implements Runnable{
    volatile int v1=10;

    private String name;

    public Task(String name) {
        this.name=name;
    }

    public  synchronized void  run() {
        v1=v1+10;
        System.out.println(name +"is entered "+v1);

    }

}
public class ThreadPoolTest {

    public static void main(String[] args) {
        Runnable r1 = new Task("Thread 1"); 
        Runnable r2 = new Task("Thread 2"); 
        Runnable r3 = new Task("Thread 3"); 

        ExecutorService executor = Executors.newFixedThreadPool(5);

        executor.execute(r1);
        executor.execute(r2);
        executor.execute(r3);

        executor.shutdown();  
    }
}

outPut:

Thread 1is entered 20
Thread 2is entered 20
Thread 3is entered 20

but if we change from volatile to static its giving below output:

Thread 1is entered 20
Thread 3is entered 30
Thread 2is entered 40

【问题讨论】:

  • 这完全有意义,因为您创建了 Task 类的 3 个实例,并且它的实例有自己的 v1 变量。因此,当您将 ot 设为静态时,所有任务都共享同一个变量。这就是为什么它会变成 20 30 40。
  • 不要change from volatile to static,而是在保留易失性的同时添加静态(即从“易失性”更改为“静态易失性”)

标签: java multithreading static threadpool thread-synchronization


【解决方案1】:

简而言之,易失性或原子变量是解决两个或多个线程尝试访问相同资源时的不一致(内存一致性错误)的解决方案(可以是静态/非-static) 同时进行。

https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

在您的情况下,您不会与多个线程共享您的任务。因此无需使用 volatile 关键字或原子变量。如果你想与多个线程共享你的变量,那么你可以使用原子变量而不是 volatile。

那么当你给变量添加静态关键字时会发生什么?

静态变量可用于该类的所有对象。

https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html

https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html

【讨论】:

    【解决方案2】:

    观察到的结果是正确的,因为您正在创建三个独立的 Task 实例,这些实例拥有自己的 name 变量副本。

    因此,尽管它们是 volatile,但值不会被其他线程更新,但执行特定实例的线程是更新 v1 字段的线程。

    如果将v1 设为静态,那么它将成为class 成员,因此显然线程将更新变量v1 的同一副本。

    我们可以使用AtomicInteger 来安全地更新和获取值,而不是在run 方法上使用synchronized

    class Task implements Runnable{
        private final AtomicInteger v1 = new AtomicInteger(10);
    
        private String name;
    
        public void run() {
           System.out.println("Thread is: " + Thread.currentThread().getName() + " " + v1.addAndGet(10));
        }
    
    }
    public class ThreadPoolTest{
    
        public static void main(String[] args) {
            Runnable r1 = new Task();
            ExecutorService executor = Executors.newFixedThreadPool(3);
            executor.execute(r1);
            executor.execute(r1);
            executor.execute(r1);
            executor.shutdown();
        }
    }
    

    【讨论】:

      【解决方案3】:

      这是因为v1 是一个实例变量,每个任务都有自己的。

      因此,在示例中,您增加了不同实例的 v1static 也不可靠,因为您正在对 Task 的实例进行同步,因此它仍然存在竞争条件(例如,即使在 static volatile 的情况下,每个线程也可能将 v1 的值读取为 10)

      可能你需要AtomicInteger

      【讨论】:

      • 是的,最好将 AtomicInteger 传递给 Runnables 的构造函数。避免使用全局/静态变量。
      猜你喜欢
      • 2018-09-18
      • 2019-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-22
      • 1970-01-01
      相关资源
      最近更新 更多