java - Loop doesn't see changed value without a print statement -


in code have loop waits state changed different thread. other thread works, loop never sees changed value. it waits forever. however, when put system.out.println statement in loop, works! why?


the following example of code:

class myhouse {     boolean pizzaarrived = false;      void eatpizza() {         while (pizzaarrived == false) {             //system.out.println("waiting");         }          system.out.println("that delicious!");     }      void deliverpizza() {         pizzaarrived = true;     } } 

while while loop running, call deliverpizza() different thread set pizzaarrived variable. loop works when uncomment system.out.println("waiting"); statement. what's going on?

the jvm allowed assume other threads not change pizzaarrived variable during loop. in other words, can hoist pizzaarrived == false test outside loop, optimizing this:

while (pizzaarrived == false) {} 

into this:

if (pizzaarrived == false) while (true) {} 

which infinite loop.

to ensure changes made 1 thread visible other threads must add synchronization between threads. simplest way make shared variable volatile:

volatile boolean pizzaarrived = false; 

making variable volatile guarantees different threads see effects of each other's changes it. prevents jvm caching value of pizzaarrived or hoisting test outside loop. instead, must read value of real variable every time.

(more formally, volatile creates happens-before relationship between accesses variable. means all other work thread did before delivering pizza visible thread receiving pizza, if other changes not volatile variables.)

synchronized methods used principally implement mutual exclusion (preventing 2 things happening @ same time), have same side-effects volatile has. using them when reading , writing variable way make changes visible other threads:

class myhouse {     boolean pizzaarrived = false;      void eatpizza() {         while (getpizzaarrived() == false) {}         system.out.println("that delicious!");     }      synchronized boolean getpizzaarrived() {         return pizzaarrived;     }      synchronized void deliverpizza() {         pizzaarrived = true;     } } 

the effect of print statement

system.out printstream object. methods of printstream synchronized this:

public void println(string x) {     synchronized (this) {         print(x);         newline();     } } 

the synchronization prevents pizzaarrived being cached during loop. strictly speaking, both threads must synchronize on same object guarantee changes variable visible. (for example, calling println after setting pizzaarrived , calling again before reading pizzaarrived correct.) if 1 thread synchronizes on particular object, jvm allowed ignore it. in practice, jvm not smart enough prove other threads won't call println after setting pizzaarrived, assumes might. therefore, cannot cache variable during loop if call system.out.println. that's why loops work when have print statement, although not correct fix.

using system.out not way cause effect, 1 people discover often, when trying debug why loop doesn't work!


the bigger problem

while (pizzaarrived == false) {} busy-wait loop. that's bad! while waits, hogs cpu, slows down other applications, , increases power usage, temperature, , fan speed of system. ideally, loop thread sleep while waits, not hog cpu.

here ways that:

using wait/notify

a low-level solution use wait/notify methods of object:

class myhouse {     boolean pizzaarrived = false;      void eatpizza() {         synchronized (this) {             while (!pizzaarrived) {                 try {                     this.wait();                 } catch (interruptedexception e) {}             }         }          system.out.println("that delicious!");     }      void deliverpizza() {         synchronized (this) {             pizzaarrived = true;             this.notifyall();         }     } } 

in version of code, loop thread calls wait(), puts thread sleep. not use cpu cycles while sleeping. after second thread sets variable, calls notifyall() wake any/all threads waiting on object. having pizza guy ring doorbell, can sit down , rest while waiting, instead of standing awkwardly @ door.

when calling wait/notify on object must hold synchronization lock of object, above code does. can use object long both threads use same object: here used this (the instance of myhouse). usually, 2 threads not able enter synchronized blocks of same object simultaneously (which part of purpose of synchronization) works here because thread temporarily releases synchronization lock when inside wait() method.

blockingqueue

a blockingqueue used implement producer-consumer queues. "consumers" take items front of queue, , "producers" push items on @ back. example:

class myhouse {     final blockingqueue<object> queue = new linkedblockingqueue<>();      void eatfood() throws interruptedexception {         // take next item queue (sleeps while waiting)         object food = queue.take();         // ,         system.out.println("eating: " + food);     }      void deliverpizza() throws interruptedexception {         // in producer threads, push items on queue.         // if there space in queue can return immediately;         // consumer thread(s) later         queue.put("a delicious pizza");     } } 

note: put , take methods of blockingqueue can throw interruptedexceptions, checked exceptions must handled. in above code, simplicity, exceptions rethrown. might prefer catch exceptions in methods , retry put or take call sure succeeds. apart 1 point of ugliness, blockingqueue easy use.

no other synchronization needed here because blockingqueue makes sure threads did before putting items in queue visible threads taking items out.

executors

executors ready-made blockingqueues execute tasks. example:

// "singlethreadexecutor" has 1 work thread , unlimited queue executorservice executor = executors.newsinglethreadexecutor();  runnable eatpizza = () -> { system.out.println("eating delicious pizza"); }; runnable cleanup = () -> { system.out.println("cleaning house"); };  // submit tasks executed on work thread executor.execute(eatpizza); executor.execute(cleanup); // continue without needing wait tasks finish 

for details see doc executor, executorservice, , executors.

event handling

looping while waiting user click in ui wrong. instead, use event handling features of ui toolkit. in swing, example:

jlabel label = new jlabel(); jbutton button = new jbutton("click me"); button.addactionlistener((actionevent e) -> {     // event listener run when button clicked.     // don't need loop while waiting.     label.settext("button clicked"); }); 

because event handler runs on event dispatch thread, doing long work in event handler blocks other interaction ui until work finished. slow operations can started on new thread, or dispatched waiting thread using 1 of above techniques (wait/notify, blockingqueue, or executor). can use swingworker, designed this, , automatically supplies background worker thread:

jlabel label = new jlabel(); jbutton button = new jbutton("calculate answer");  // add click listener button button.addactionlistener((actionevent e) -> {      // defines myworker swingworker result type string:     class myworker extends swingworker<string,void> {         @override         public string doinbackground() throws exception {             // method called on background thread.             // can long work here without blocking ui.             // example:             thread.sleep(5000);             return "answer 42";         }          @override         protected void done() {             // method called on swing thread once work done             string result;             try {                 result = get();             } catch (exception e) {                 throw new runtimeexception(e);             }             label.settext(result); // display "answer 42"         }     }      // start worker     new myworker().execute(); }); 

timers

to perform periodic actions, can use java.util.timer. easier use writing own timing loop, , easier start , stop. demo prints current time once per second:

timer timer = new timer(); timertask task = new timertask() {     @override     public void run() {         system.out.println(system.currenttimemillis());     } }; timer.scheduleatfixedrate(task, 0, 1000); 

each java.util.timer has own background thread used execute scheduled timertasks. naturally, thread sleeps between tasks, not hog cpu.

in swing code, there javax.swing.timer, similar, executes listener on swing thread, can safely interact swing components without needing manually switch threads:

jframe frame = new jframe(); frame.setdefaultcloseoperation(jframe.exit_on_close); timer timer = new timer(1000, (actionevent e) -> {     frame.settitle(string.valueof(system.currenttimemillis())); }); timer.setrepeats(true); timer.start(); frame.setvisible(true); 

other ways

if writing multithreaded code, worth exploring classes in these packages see available:

and see concurrency section of java tutorials. multithreading complicated, there lots of available!


Comments

Popular posts from this blog

ios - MKAnnotationView layer is not of expected type: MKLayer -

ZeroMQ on Windows, with Qt Creator -

unity3d - Unity SceneManager.LoadScene quits application -