volatile变量未提供预期的输出

时间:2019-02-22 05:57:31

标签: java multithreading static threadpool thread-synchronization

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

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

3 个答案:

答案 0 :(得分:4)

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

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

如果将v1设为静态,则它将成为class成员,因此很明显,线程将更新变量v1的相同副本。

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

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

答案 1 :(得分:2)

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

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

可能您需要AtomicInteger

答案 2 :(得分:1)

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

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

对于您来说,您与多个线程共享任务。,因此无需使用volatile关键字或Atomic变量。如果要与多个线程共享变量,则可以在volatile上使用Atomic变量。

然后,当您将static关键字添加到变量时会发生什么情况?

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

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

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