volatile 变量没有给出预期的输出

volatile variable is not giving expected output

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

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 实例,它们拥有自己的 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();
    }
}

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

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

可能你需要AtomicInteger

简而言之,volatile或Atomic Variables是解决当两个或多个线程试图访问相同的不一致(Memory Consistency Errors)的解决方案资源(可以是static/non-static)同时。

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

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

那么,当你向变量添加 static 关键字时会发生什么?

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

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

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