By dontocsata


2011-02-08 15:25:07 8 Comments

My teacher in an upper level java class on threading said something that I wasn't sure of.

He stated that the following code would not necessarily update the ready variable. According to him, the two threads don't necessarily share the static variable, specifically in the case when each thread (main thread versus ReaderThread) is running on its own processor and therefore doesn't share the same registers/cache/etc and one CPU won't update the other.

Essentially, he said it is possible that ready is updated in the main thread, but NOT in the ReaderThread, so that ReaderThread will loop infinitely. He also claimed it was possible for the program to print '0' or '42'. I understand how '42' could be printed, but not '0'. He mentioned this would be the case when the number variable is set to the default value.

I thought perhaps it is not guaranteed that the static variable is updated between the threads, but this strikes me as very odd for Java. Does making ready volatile correct this problem?

He showed this code:

public class NoVisibility {  
    private static boolean ready;  
    private static int number;  
    private static class ReaderThread extends Thread {   
        public void run() {  
            while (!ready)   Thread.yield();  
            System.out.println(number);  
        }  
    }  
    public static void main(String[] args) {  
        new ReaderThread().start();  
        number = 42;  
        ready = true;  
    }  
}

7 comments

@NuOne T Attygalle 2018-04-19 14:31:33

When you initialize static primitive type variable java default assigns a value for static variables

public static int i ;

when you define the variable like this the default value of i = 0; thats why there is a possibility to get you 0. then the main thread updates the value of boolean ready to true. since ready is a static variable , main thread and the other thread reference to the same memory address so the ready variable change. so the secondary thread get out from while loop and print value. when printing the value initialized value of number is 0. if the thread process has passed while loop before main thread update number variable. then there is a possibility to print 0

@bestsss 2011-02-11 00:06:31

@dontocsata you can go back to your teacher and school him a little :)

few notes from the real world and regardless what you see or be told. Please NOTE, the words below are regarding this particular case in the exact order shown.

The following 2 variable will reside on the same cache line under virtually any know architecture.

private static boolean ready;  
private static int number;  

Thread.exit (main thread) is guaranteed to exit and exit is guaranteed to cause a memory fence, due to the thread group thread removal (and many other issues). (it's a synchronized call, and I see no single way to be implemented w/o the sync part since the ThreadGroup must terminate as well if no daemon threads are left, etc).

The started thread ReaderThread is going to keep the process alive since it is not a daemon one! Thus ready and number will be flushed together (or the number before if a context switch occurs) and there is no real reason for reordering in this case at least I can't even think of one. You will need something truly weird to see anything but 42. Again I do presume both static variables will be in the same cache line. I just can't imagine a cache line 4 bytes long OR a JVM that will not assign them in a continuous area (cache line).

@Jed Wesley-Smith 2011-02-11 06:19:16

@bestsss while that is all true today, it relies on current JVM implementation and hardware architecture for its truth, not on the semantics of the program. This means that the program is still broken, even though it may work. It is easy to find a trivial variant of this example that does actually fail in the ways specified.

@bestsss 2011-02-11 06:26:56

I did say that it doesn't follow the spec, however as a teacher at least find a suitable example that can actually fail on some commodity architecture, so the example is sort of real.

@bestsss 2011-02-11 06:34:07

side note: reminds me how java.util.concurrent.Exchanger tries to avoid the fake isolation(need a link, i know) with bumping the cache line by adding 15 extra longs.

@Lawrence Dol 2011-02-18 04:40:17

Possibly the worst advice on writing thread-safe code I've seen.

@bestsss 2011-02-21 01:03:42

@Software Monkey, where do you see any piece of advice? The post explains what actually happens vs the fictional case given by the teacher, it's easy to write an example that features the problem instead of being solely hypothetical.

@Lawrence Dol 2011-02-21 02:33:05

@Bestsss: The simple answer to your question is simply this: "Code to the specification and document, not to the side-effects of your particular system or implementation". This is especially important in a virtual-machine platform designed to be agnostic of the underlying hardware.

@bestsss 2011-02-21 20:50:53

@Software Monkey, perhaps I am being stupid yet I still see no advice, whatsoever. However, I do believe that a teacher shall provide a working example when illustrates any problem since the one given the to OP is virtually broken on any known hardware. If you reread what I say: I never advise to code in a sloppy way, expecting the data will fall in the same cache line. Yet, coding in a way NOT avoiding it (having fake isolation) is a major, major problem.

@Lawrence Dol 2011-02-21 20:54:32

@Bestsss: The teachers point is that (a) the code might well work when you test it, and (b) the code is broken because it depends on hardware operation and not the guarantees of the spec. The point is that it looks like it's OK, but it's not OK.

@bestsss 2011-02-21 21:16:19

@Software Monkey, well I said already, it's not difficult to give an example that looks good and it can fail, otherwise a hundred people testing the code for real and never (even once) getting a fluke. Most students rightfully preclude the teacher sucks and move on.

@Nathan Hughes 2011-02-08 15:31:00

There is no visibility issue specific to static variables. There is a visibility issue imposed by the JVM's memory model. Here's an article talking about the memory model and how writes become visible to threads. You can't count on changes one thread makes becoming visible to other threads in a timely manner (actually the JVM has no obligation to make those changes visible to you at all), unless you establish a happens-before relationship, here's a quote from that link (supplied in the comment by Jed Wesley-Smith):

Chapter 17 of the Java Language Specification defines the happens-before relation on memory operations such as reads and writes of shared variables. The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation. The synchronized and volatile constructs, as well as the Thread.start() and Thread.join() methods, can form happens-before relationships. In particular:

  • Each action in a thread happens-before every action in that thread that comes later in the program's order.

  • An unlock (synchronized block or method exit) of a monitor happens-before every subsequent lock (synchronized block or method entry) of that same monitor. And because the happens-before relation is transitive, all actions of a thread prior to unlocking happen-before all actions subsequent to any thread locking that monitor.

  • A write to a volatile field happens-before every subsequent read of that same field. Writes and reads of volatile fields have similar memory consistency effects as entering and exiting monitors, but do not entail mutual exclusion locking.

  • A call to start on a thread happens-before any action in the started thread.

  • All actions in a thread happen-before any other thread successfully returns from a join on that thread.

@TREE 2011-02-08 16:42:19

In practice, "in a timely manner" and "ever" are synonymous. It would be very possible for the above code to never terminate.

@TREE 2011-02-08 16:43:28

Also, this demonstrates another anti-pattern. Don't use volatile to protect more than one piece of shared state. Here, number and ready are two pieces of state, and to update/read them both consistently, you need actual synchronization.

@Steve Kuo 2011-02-08 17:50:42

Don't forget final. final variables are always visible to all threads.

@Jed Wesley-Smith 2011-02-08 22:24:41

The part about eventually becoming visible is wrong. Without any explicit happens-before relationship there is no guarantee that any write will ever be seen by another thread, as the JIT is quite within its rights to foist the read into a register and then you'll never see any update. Any eventual load is luck and should not be relied on.

@Nathan Hughes 2011-02-09 15:40:30

@Jed Wesley-Smith: You are right, there is no guarantee. I had meant 'eventually' as in 'if you wait long enough' where there is no bound on what long enough is, but it could be clearer. I'll change the answer.

@Jed Wesley-Smith 2011-02-09 21:59:07

"unless you use the volatile keyword or synchronize." should read "unless there is a relevant happens-before relationship between the writer and reader" and this link: download.oracle.com/javase/6/docs/api/java/util/concurrent/…

@bestsss 2011-02-11 00:05:03

@Jed Wesley-Smith, Thread.exit guarantees a memory fence, it has a sync. call; and you can see my reply for smth funny.

@bestsss 2011-02-11 06:09:19

@TREE, volatile protects nothing, volatile ensures ordering, you need CAS to ensure atomic access and CAS/lock-free algorithms, structures are not an anti-pattern.

@Jed Wesley-Smith 2011-02-11 06:20:55

@bestsss Thread.exit doesn't guarantee anything, it has the consequence of making a happens-before edge in its current implementation on the Sun Java6 VM. That is not something you can rely on.

@bestsss 2011-02-11 06:24:55

@Jed Wesley-Smith, I know the spec. but getting the thread removed out the thread group (which is also in the spec) can't ever happen w/o the happens-before part (i.e. a memory fence).

@Jed Wesley-Smith 2011-02-12 03:42:09

@bestsss link for the spec? I can't seem to find any docs saying this must happen.

@bestsss 2011-02-12 09:04:47

@ Jed Wesley-Smith, ThreadGroup.setDaemon A daemon thread group is automatically destroyed when its last thread is stopped or its last thread group is destroyed. You cant define "last" w/o the happens-before. Sun is notorious for not updating docs, though

@Jed Wesley-Smith 2011-02-13 03:46:48

@bestsss well spotted. Unfortunately ThreadGroup is broken in so many ways.

@bestsss 2011-02-13 09:51:14

@Jed Wesley-Smith, ofc I know that (the broken part) but well, I find it immensely useful (for OSGi-alike stuff)

@Bert F 2011-02-08 15:35:30

He was talking about visibility and not to be taken too literally.

Static variables are indeed shared between threads, but the changes made in one thread may not be visible to another thread immediately, making it seem like there are two copies of the variable.

This article presents a view that is consistent with how he presented the info:

First, you have to understand a little something about the Java memory model. I've struggled a bit over the years to explain it briefly and well. As of today, the best way I can think of to describe it is if you imagine it this way:

  • Each thread in Java takes place in a separate memory space (this is clearly untrue, so bear with me on this one).

  • You need to use special mechanisms to guarantee that communication happens between these threads, as you would on a message passing system.

  • Memory writes that happen in one thread can "leak through" and be seen by another thread, but this is by no means guaranteed. Without explicit communication, you can't guarantee which writes get seen by other threads, or even the order in which they get seen.

...

thread model

But again, this is simply a mental model to think about threading and volatile, not literally how the JVM works.

@biziclop 2011-02-08 15:34:30

They are "shared" of course in the sense that they both refer to the same variable, but they don't necessarily see each other's updates. This is true for any variable, not just static.

And in theory, writes made by another thread can appear to be in a different order, unless the variables are declared volatile or the writes are explicitly synchronized.

@axtavt 2011-02-08 15:33:26

Basically it's true, but actually the problem is more complex. Visibility of shared data can be affected not only by CPU caches, but also by out-of-order execution of instructions.

Therefore Java defines a Memory Model, that states under which circumstances threads can see consistent state of the shared data.

In your particular case, adding volatile guarantees visibility.

@Kirk Woll 2011-02-08 15:27:40

Within a single classloader, static fields are always shared. To explicitly scope data to threads, you'd want to use a facility like ThreadLocal.

Related Questions

Sponsored Content

24 Answered Questions

24 Answered Questions

29 Answered Questions

[SOLVED] Why are static variables considered evil?

  • 2011-08-11 13:14:37
  • Vamsi Emani
  • 203759 View
  • 546 Score
  • 29 Answer
  • Tags:   java static

16 Answered Questions

[SOLVED] Are static class variables possible?

36 Answered Questions

[SOLVED] Differences between HashMap and Hashtable?

33 Answered Questions

[SOLVED] Difference between wait() and sleep()

32 Answered Questions

[SOLVED] What is the difference between a process and a thread?

41 Answered Questions

[SOLVED] "implements Runnable" vs "extends Thread" in Java

36 Answered Questions

[SOLVED] Difference between static class and singleton pattern?

Sponsored Content