简体   繁体   中英

volatile variable is not giving expected output

I read that volatile variable copy will shared by all the threads and once completion of the execution, the update value will get by every thread,But in the following program using thread pool not giving the output which I expected, can any one tell me the reason?

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

The observed result is correct as you are creating three separate Task instances who are having their own copy of the name variable.

So although they are volatile the values are not updated by other threads the thread executing the particular instance is the one which is updating the v1 field.

If the v1 is made static then it becomes a class member so obviously the threads will update the same copy of the variable v1 .

Instead of using synchronized on the run method, we can use the AtomicInteger to safely update and fetch the value.

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

This is because v1 is an instance variable and each task has its own.

So in the example you incremented v1 of different instances. static also is not reliable since you are sinchronizing on the instance of a Task so it still has a race condition (eg each thread may read v1 's value to be 10 even in case of static volatile )

Probably you need AtomicInteger

To be short, volatile or Atomic Variables are the solutions to solve the inconsistency( Memory Consistency Errors ) when two or more threads try to access the same resource(can be static/non-static) simultaneously.

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

In your case, you are not sharing your task with more than one Threads. so no need of using volatile keyword or Atomic variables. if you want to share your variable with more than one thread, then you can use Atomic variables over volatile.

then what happens, when you add static keyword to a variable?

static variables are available to all objects of that class.

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM