Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to modify a non-volatile variable such that another thread is able to "see" the update?

I have a Thread-X which reads a non-volatile variable every second, doing so without any means of synchronization.

Now I was wondering is there some way to modify that non-volatile variable on Thread-Y such that Thread-Y's write would be (eventually) visible on Thread-X ?

public class Test {
    private static boolean double = 1; // this variable is
                                             // strictly not volatile

    public static void main(String args[]) {
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                while (true) {
                    System.out.println(variable);
                    try {
                        java.lang.Thread.currentThread().sleep(1000);
                    } catch (java.lang.InterruptedException e) {
                    }
                }
            }
        }).start(); // line 17
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                // the task is to change variable to "2" such the write
                // is (eventually) registered by the other threads

                // allowed to use any synchronization/locking techniques
                // required, but line 1 to line 17 must not be changed
            }
        }).start();
    }
}

Is it possible to modify a non-volatile variable such that another thread which reads it without any synchronization techniques (raw read) is able to "see" the update eventually?

Background:

I need to read a variable from a large number of threads, for an infinite amount of times.

From what I understand (correct me if I'm wrong), on most cpus (e.g. x86) reads of volatile variables are "almost totally free" but not "totally free".

Now since I have an infinite number of reads from an infinite number of threads, I would love the variable to be non-volatile. However, once in a blue moon the variable needs to be updated. In my use-case, it really doesn't matter how expensive it takes to update that variable, but that update must eventually be readable by the reader-threads.

Solutions:

Based on Tomasz's comment, I've built this solution and I was wondering is Solution-1 flawed or is it solid?

public class Solution1 {
    private static double variable = 1; // this variable is
                                        // strictly not volatile

    public static void main(String args[]) {
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                while (true) {
                    System.out.println(variable);
                    try {
                        java.lang.Thread.currentThread().sleep(1000);
                    } catch (java.lang.InterruptedException e) {
                    }
                }
            }
        }).start(); // line 17
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                variable = 2;
                // writer-thread now terminates,
                // is it guaranteed that when it
                // "terminates successfully", variable
                // is updated on the reader-thread ?
            }
        }).start();
    }
}

Based on Joonas's comment, I've built this solution and I was wondering is Solution-2 flawed or is it solid?

public class Solution2 {
    private static double variable = 1; // this variable is
                                        // strictly not volatile
    private static volatile boolean lock = false;

    public static void main(String args[]) {
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                while (true) {
                    System.out.println(variable);
                    try {
                        java.lang.Thread.currentThread().sleep(1000);
                    } catch (java.lang.InterruptedException e) {
                    }
                }
            }
        }).start(); // line 17
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                variable = 2;
                lock = false; // does this line guarantee
                                // that other threads will now 
                                // see the update to variable (piggypacking)?

                // now let's assume this thread doesn't terminate
            }
        }).start();
    }
}
like image 869
Pacerier Avatar asked Dec 22 '22 01:12

Pacerier


2 Answers

Is it possible to modify a non-volatile variable such that another thread which reads it without any synchronization techniques (raw read) is able to "see" the update eventually?

No. Some synchronization technique must be used, because otherwise the (JIT) compiler is allowed to optimize your line to System.out.println(false); (if false is the value first seen by that thread). That is, it can optimize reading the variable away.

I have no idea how likely it would actually do that, but it's okay according to the Java Memory Model, so your options are:

  • volatile. This is probably the simplest and lightest option for your case.
  • AtomicBoolean. Some discussion of it vs. volatile here and here.
  • synchronized block. Overkill in this case.
  • java.util.concurrent.locks.Lock. More features than in synchronized.
  • Thread.join(). Not useful in this case (the reading thread would wait for the writer to terminate).
  • Piggybacking. Don't even think about it. Too many things that may and will go wrong.

Just use volatile and let the JVM worry about implementing it efficiently. It's not expensive.

like image 154
Joonas Pulakka Avatar answered Apr 27 '23 22:04

Joonas Pulakka


Quoting Synchronization and the Java Memory Model from Concurrent Programming in Java by Doug Lea:

Changes to fields made by one thread are guaranteed to be visible to other threads only under the following conditions:

  • A writing thread releases a synchronization lock and a reading thread subsequently acquires that same synchronization lock.

  • If a field is declared as volatile, any value written to it is flushed and made visible by the writer thread before the writer thread performs any further memory operation (i.e., for the purposes at hand it is flushed immediately). Reader threads must reload the values of volatile fields upon each access.

  • The first time a thread accesses a field of an object, it sees either the initial value of the field or a value since written by some other thread.

  • As a thread terminates, all written variables are flushed to main memory. For example, if one thread synchronizes on the termination of another thread using Thread.join, then it is guaranteed to see the effects made by that thread (see §4.3.2).

Last two options do not apply to your situations so you need either volatile or synchronized, sorry. Note that AtomicInteger.get() simply returns volatile value, so you get nothing except extra layer.

like image 44
Tomasz Nurkiewicz Avatar answered Apr 27 '23 23:04

Tomasz Nurkiewicz