4

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();
    }
}
Community
  • 1
  • 1
Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • 2
    You can make the variable volatile. That's what it's for. Also, polling is often a poor solution and perhaps there's a cleaner alternative to polling. – David Heffernan Mar 06 '12 at 09:09
  • @DavidHeffernan I'm using polling to demonstrate the question.. because its rather hard to explain it clearly without an example. – Pacerier Mar 06 '12 at 09:11

3 Answers3

5

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.

Community
  • 1
  • 1
Joonas Pulakka
  • 36,252
  • 29
  • 106
  • 169
  • I'm trying to get "totally free" reads, and I'm prepared to do a very expensive write in exchange for "totally free" reads. Is it true that for this problem, there is no better solution than declaring it as volatile? – Pacerier Mar 06 '12 at 09:14
  • 3
    Well. There's a technique called "piggypacking", which is based on the fact that accessing *any* volatile variable synchronizes local copies of *all* cached variables with main memory. Therefore, it is occasionally possible to write to one variable without synchronization and then take advantage of subsequent synchronization on a different variable to ensure that main memory is updated with both variables. [See this for reference](http://www.javamex.com/tutorials/synchronization_piggyback.shtml). It's very brittle and shouldn't be used for anything, but just FYI... – Joonas Pulakka Mar 06 '12 at 09:18
  • I'm not quite sure what do you mean by "main memory". When you say "main memory", do you mean the "memory" used by all the threads? – Pacerier Mar 06 '12 at 09:20
  • 1
    @Pacerier: Main memory here is the RAM memory of the computer. Cache is the processor cache. The existence of these two memories is the reason why `volatile` is needed in the first place: if all processors would access RAM directly, then there would be no memory / variable visibility issues (but it would be slow). See [What every programmer should know about memory](http://lwn.net/Articles/250967/). – Joonas Pulakka Mar 06 '12 at 09:23
  • Btw take a look at the update (solution-2), do you think its solid? – Pacerier Mar 06 '12 at 09:44
  • @Pacerier: I don't think the (solution-2) can get around the fact that the JIT compiler is allowed to optimize *reading* of `variable` away, replacing it with a local variable. No amount of writing can fix it, if it's not being read! Using piggypacking correctly and effectively would require *really, really* understanding the JMM, from inside out. I'm not that much an expert. I suggest just using volatile, and then measuring where your bottlenecks are. Otherwise you're quite likely trying to optimize the wrong thing. – Joonas Pulakka Mar 06 '12 at 09:59
  • I'm using it for a DEBUG variable, it's read roughly once per method call for 99% of the methods I write. It's not a "static final" because I want it to be changeable dynamically. Imagine there are x number of method calls in the lifecycle of a program, that's x number of reads on the debug variable... – Pacerier Mar 06 '12 at 10:05
  • Btw are you aware if terminating the thread would enforce an update (solution-1) ? – Pacerier Mar 06 '12 at 10:07
  • 1
    Just terminating thread B has no effect on thread A. Tomasz is talking about *joining* threads. Sorry, you simply can't get around the fact that the JVM JIT compiler is allowed to optimize *reading* `variable` away unless you're using some synchronization construct (or `Thread.join()`; the *reading* thread must call `join` on the *writing* thread which is about to terminate). – Joonas Pulakka Mar 06 '12 at 10:17
3

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.

Community
  • 1
  • 1
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • Regarding point 4, do you mean that if the "writer-thread" terminates, the "reader-thread" will see the update to the **non-volatile** variable, Or am I misunderstanding something? – Pacerier Mar 06 '12 at 09:17
  • @Pacerier: well, it's not me, it's Doug Lea ;-). The example mentions `Thread.join()`, I don't think it means "*every change made by terminated thread is immediately visible by all other threads*". Also do you really consider starting new thread just to modify a single variable an option? – Tomasz Nurkiewicz Mar 06 '12 at 09:21
  • 1
    Yes if it works, because for my use-case I seldom need to write, but there's an *infinite* amount of reads from a large number of threads. Now declaring the variable as volatile would penalize all reader threads, and I would rather "heavily penalize" the writer-thread. – Pacerier Mar 06 '12 at 09:27
  • @Pacerier: when it comes to `volatile` read performance, have a look at: http://stackoverflow.com/questions/4633866 – Tomasz Nurkiewicz Mar 06 '12 at 09:31
  • Thanks for the link =D Btw take a look at the update (solution-1), do you think its working? – Pacerier Mar 06 '12 at 09:44
3

I'm trying to get "totally free" reads, and I'm prepared to do a very expensive write in exchange for "totally free" reads. Is it true that for this problem, there is no better solution than declaring it as volatile?

Nothing is totally free. A read of a volatile variable which is not being changed can be sub-nanosecond which may be more than fast enough. If you read after a write it can take 5 nano-seconds. The branch could take 5 - 10 nanoseconds regardless of your "free" read and if you do something as simple as taking the time e.g. with System.nanoTime(), this can take 20 - 180 nano-seconds depending on your OS. The cost your read should be VERY low in your list of concerns.

For example, you should be worried about whether your code is warmed up so it is compiled instead of interpreted. This can make far, far more difference (see below)


volatile can be needed in a number of situations, but I don't believe you have one of them.

The most common situation is as @Joonas Pulakka mentions where the JIT optimises the field in a manner you don't want i.e. it stops reading the value and makes it a local variable, because its not volatile.

This optimisation can happen after a loop iterates 10,000 times in quick succession. In your case, it is not quick succession, but over 2.7 hours so the JIT might never optimise the code.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130