I've been trying to make a binary semaphore that will be able to safely block execution of a method running on the event dispatch thread (EDT) without actually blocking the thread from handling more events. This may initially seem impossible, but Java has some built-in functionality related to this, but I can't quite get it to work.
Use Case
Currently, if you show a modal swing dialog from the EDT, it will appear to block the EDT (because your method that displayed the modal dialog will not continue onto the next line until the dialog is closed), but really there's some under-the-hood magic that makes the EDT enter a new event loop which will continue to dispatch events until the modal dialog is closed.
My team currently has applications that are very slowly migrating from swing to JavaFX (a somewhat tricky transition) and I wanted to be able to display modal JavaFX dialogs from the AWT event dispatch thread in the same way that swing modal dialogs can be shown. It seemed like having some sort of EDT-safe semaphore would meet this use case and likely come in handy for other uses down the road.
Approach
java.awt.EventQueue.createSecondaryLoop() is a method that creates a SecondaryLoop object, which you can then use to kick off a new event handling loop. When you call SecondaryLoop.enter(), the call will block while it processes a new event loop (note that the call blocks, but the thread is not blocked because it is continuing in an event processing loop). The new event loop will continue until you call SecondaryLoop.exit() (that's not entirely true, see my related SO question).
So I've created a semaphore where a blocking call to acquire results in waiting on a latch for a normal thread, or entering a secondary loop for the EDT. Each blocking call to acquire also adds an unblocking operation to be called when the semaphore is freed (for a normal thread, it just decrements the latch, for the EDT, it exits the secondary loop).
Here is my code:
import java.awt.EventQueue;
import java.awt.SecondaryLoop;
import java.awt.Toolkit;
import java.util.Stack;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
#SuppressWarnings("serial")
public class EventDispatchThreadSafeBinarySemaphore extends Semaphore{
/** Operations used to unblock threads when a semaphore is released.
* Must be a stack because secondary loops have to be exited in the
* reverse of the order in which they were entered in order to unblock
* the execution of the method that entered the loop.
*/
private Stack<Runnable> releaseOperations = new Stack<>();
private boolean semaphoreAlreadyAcquired = false;
public EventDispatchThreadSafeBinarySemaphore() {
super(0);
}
#Override
public boolean isFair() {
return false;
}
#Override
public void acquire() throws InterruptedException {
Runnable blockingOperation = () -> {};
synchronized(this) {
if(semaphoreAlreadyAcquired) {
//We didn't acquire the semaphore, need to set up an operation to execute
//while we're waiting on the semaphore and an operation for another thread
//to execute in order to unblock us when the semaphore becomes available
if(EventQueue.isDispatchThread()) {
//For the EDT, we don't want to actually block, rather we'll enter a new loop that will continue
//processing AWT events.
SecondaryLoop temporaryAwtLoop = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop();
releaseOperations.add(() -> temporaryAwtLoop.exit());
blockingOperation = () -> {
if(!temporaryAwtLoop.enter()) {
//I don't think we'll run into this, but I'm leaving this here for now for debug purposes
System.err.println("Failed to enter event loop");
}
};
}
else {
//Non-dispatch thread is a little simpler, we'll just wait on a latch
CountDownLatch blockedLatch = new CountDownLatch(1);
releaseOperations.add(() -> blockedLatch.countDown());
blockingOperation = () -> {
try {
blockedLatch.await();
} catch (InterruptedException e) {
//I'll worry about handling this better once I have the basics figured out
e.printStackTrace();
}
};
}
}
else {
semaphoreAlreadyAcquired = true;
}
}
//This part must be executed outside of the synchronized block so that we don't block
//the EDT if it tries to acquire the semaphore while this statement is blocked
blockingOperation.run();
}
#Override
public void release() {
synchronized(this) {
if(releaseOperations.size() > 0) {
//Release the last blocked thread
releaseOperations.pop().run();
}
else {
semaphoreAlreadyAcquired = false;
}
}
}
}
And here is my relevant JUnit test code (I apologize for the large size, this is the smallest minimum verifiable example I've been able to come up with so far):
public class TestEventDispatchThreadSafeBinarySemaphore {
private static EventDispatchThreadSafeBinarySemaphore semaphore;
//See https://stackoverflow.com/questions/58192008/secondaryloop-enter-not-blocking-until-exit-is-called-on-the-edt
//for why we need this timer
private static Timer timer = new Timer(500, null);
#BeforeClass
public static void setupClass() {
timer.start();
}
#Before
public void setup() {
semaphore = new EventDispatchThreadSafeBinarySemaphore();
}
#AfterClass
public static void cleanupClass() {
timer.stop();
}
//This test passes just fine
#Test(timeout = 1000)
public void testBlockingAcquireReleaseOnEDT() throws InterruptedException {
semaphore.acquire();
CountDownLatch edtCodeStarted = new CountDownLatch(1);
CountDownLatch edtCodeFinished = new CountDownLatch(1);
SwingUtilities.invokeLater(() -> {
//One countdown to indicate that this has begun running
edtCodeStarted.countDown();
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//This countdown indicates that it has finished running
edtCodeFinished.countDown();
});
//Ensure that the code on the EDT has started
edtCodeStarted.await();
assertEquals("Code on original AWT event thread should still be blocked", 1, edtCodeFinished.getCount());
//Ensure that things can still run on the EDT
CountDownLatch edtActiveCheckingLatch = new CountDownLatch(1);
SwingUtilities.invokeLater(() -> edtActiveCheckingLatch.countDown());
//If we get past this line, then we know that the EDT is live even though the
//code in the invokeLater call is blocked
edtActiveCheckingLatch.await();
assertEquals("Code on original AWT event thread should still be blocked", 1, edtCodeFinished.getCount());
semaphore.release();
//If we get past this line, then the code on the EDT got past the semaphore
edtCodeFinished.await();
}
//This test fails intermittently, but so far only after the previous test was run first
#Test(timeout = 10000)
public void testConcurrentAcquiresOnEDT() throws InterruptedException {
int numThreads =100;
CountDownLatch doneLatch = new CountDownLatch(numThreads);
try {
semaphore.acquire();
//Queue up a bunch of threads to acquire and release the semaphore
//as soon as it becomes available
IntStream.range(0, numThreads)
.parallel()
.forEach((threadNumber) ->
SwingUtilities.invokeLater(() -> {
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
semaphore.release();
//Count down the latch to indicate that the thread terminated
doneLatch.countDown();
}
})
);
semaphore.release();
doneLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
The Problem
testConcurrentAcquiresOnEDT will sometimes pass and sometimes fail. I believe that I know why. I dug into the Java source code and in WaitDispatchSupport (the concrete implementation of SecondaryLoop), the loop basically continues dispatching events until a flag called keepBlockingEDT is cleared. It will check this between events. When I call exit, it will clear that flag and send an event to wakeup the event queue in case it was waiting for more events. However, it will not cause the enter() method to immediately exit (and I don't think there's anyway it possibly could).
So here's how the deadlock results:
The main thread acquires the semaphore
The EDT thread tries to acquire the semaphore, but it is already acquired, so it:
Creates a new secondary loop
Creates a Runnable that will exit the new secondary loop and pushes it to the releaseOperations stack
Enters the secondary loop, causing execution to block (note that this last step is by necessity outside of the synchronized block
The main thread releases the semaphore, which causes the following to happen:
The releaseOperations stack is popped and it calls exit on the secondary loop
The exit call, sets the keepBlockingEDT flag for that secondary loop to be set to false
Back in the EDT, it just got done checking the keepBlockingEDT flag (right before it was set to false) and it is fetching the next event.
It turns out that the next event is another runnable that blocks on the semaphore, so it tries to acquire it
This creates another SecondaryLoop on top of the original SecondaryLoop and enters it
At this point, the original SecondaryLoop has already had it's keepBlockingEDT flag cleared and it would be able to stop blocking, except that it is currently blocked running the second SecondaryLoop. The second SecondaryLoop won't ever have exit called on it because no one actually has the semaphore acquired right now, therefore we block forever.
I've been working on this for a few days and every idea I come up with is a dead end.
I believe that I have a possible partial solution, which is to simply not allow more than one thread to be blocked on the semaphore at a time (if another thread tries to acquire it, I'll just throw an IllegalStateException). I could still have multiple secondary loops going if they each use their own semaphore, but each semaphore would create at most 1 secondary loop. I think this would work and it will meet my most likely use case just fine (because mostly I just want to show a single JavaFX modal dialog from the event thread). I just wanted to know if anyone else had other ideas because I feel like I got close to making something pretty cool, but it just doesn't quite work.
Let me know if you have any ideas. And "I'm pretty sure this is impossible and here's why..." is an acceptable answer as well.
Using a Semaphore is most likely not the correct approach. What you want is to enter nested event loops, not use blocking mechanisms. From reading the API it also appears you are over-complicating things. Again, all you need is to enter a nested event loop on one UI thread and then exit that loop once the other UI thread has completed its work. I believe the following meets your requirements:
import java.awt.EventQueue;
import java.awt.SecondaryLoop;
import java.awt.Toolkit;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import javafx.application.Platform;
import javax.swing.SwingUtilities;
public class Foo {
public static <T> T getOnFxAndWaitOnEdt(Supplier<? extends T> supplier) {
Objects.requireNonNull(supplier, "supplier");
if (!EventQueue.isDispatchThread()) {
throw new IllegalStateException("current thread != EDT");
}
final SecondaryLoop loop = Toolkit.getDefaultToolkit()
.getSystemEventQueue()
.createSecondaryLoop();
final AtomicReference<T> valueRef = new AtomicReference<>();
Platform.runLater(() -> {
valueRef.set(supplier.get());
SwingUtilities.invokeLater(loop::exit);
});
loop.enter();
return valueRef.get();
}
public static <T> T getOnEdtAndWaitOnFx(Supplier<? extends T> supplier) {
Objects.requireNonNull(supplier, "supplier");
if (!Platform.isFxApplicationThread()) {
throw new IllegalStateException(
"current thread != JavaFX Application Thread");
}
final Object key = new Object();
final AtomicReference<T> valueRef = new AtomicReference<>();
SwingUtilities.invokeLater(() -> {
valueRef.set(supplier.get());
Platform.runLater(() -> Platform.exitNestedEventLoop(key, null));
});
Platform.enterNestedEventLoop(key);
return valueRef.get();
}
}
The Platform#enterNestedEventLoop and Platform#exitNestedEventLoop methods were added in JavaFX 9 though there are equivalent internal methods in JavaFX 8. The reason AtomicReference is used is because local variables must be final or effectively final when used inside a lambda expression. However, due to the way the separate threads are notified I don't believe the volatility semantics provided by the #get() and #set(T) methods of AtomicReference is strictly needed but I've used those methods just in case.
Here's an example of using the above to show a modal JavaFX dialog from the Event Dispatch Thread:
Optional<T> optional = Foo.getOnFxAndWaitOnEdt(() -> {
Dialog<T> dialog = new Dialog<>();
// configure dialog...
return dialog.showAndWait();
});
The above utility methods are for communicating from the Event Dispatch Thread to the JavaFX Application Thread and vice versa. This is why entering a nested event loop is necessary, otherwise one of the UI threads would have to block and that would freeze the associated UI. If you're on a non-UI thread and need to run an action on a UI thread while waiting for the result the solution is much simpler:
// Run on EDT
T result = CompletableFuture.supplyAysnc(/*Supplier*/, SwingUtilities::invokeLater).join();
// Run on FX thread
T result = CompletableFuture.supplyAsync(/*Supplier*/, Platform::runLater).join();
The call to join() will block the calling thread so be sure not to call the method from either of the UI threads.
Related
In my code I have a loop that waits for some state to be changed from a different thread. The other thread works, but my loop never sees the changed value. It waits forever. However, when I put a System.out.println statement in the loop, it suddenly works! Why?
The following is an example of my code:
class MyHouse {
boolean pizzaArrived = false;
void eatPizza() {
while (pizzaArrived == false) {
//System.out.println("waiting");
}
System.out.println("That was delicious!");
}
void deliverPizza() {
pizzaArrived = true;
}
}
While the while loop is running, I call deliverPizza() from a different thread to set the pizzaArrived variable. But the loop only works when I uncomment the System.out.println("waiting"); statement. What's going on?
The JVM is allowed to assume that other threads do not change the pizzaArrived variable during the loop. In other words, it can hoist the pizzaArrived == false test outside the loop, optimizing this:
while (pizzaArrived == false) {}
into this:
if (pizzaArrived == false) while (true) {}
which is an infinite loop.
To ensure that changes made by one thread are visible to other threads you must always add some synchronization between the threads. The simplest way to do this is to make the shared variable volatile:
volatile boolean pizzaArrived = false;
Making a variable volatile guarantees that different threads will see the effects of each other's changes to it. This prevents the JVM from caching the value of pizzaArrived or hoisting the test outside the loop. Instead, it must read the value of the real variable every time.
(More formally, volatile creates a happens-before relationship between accesses to the variable. This means that all other work a thread did before delivering the pizza is also visible to the thread receiving the pizza, even if those other changes are not to volatile variables.)
Synchronized methods are used principally to implement mutual exclusion (preventing two things happening at the same time), but they also have all the same side-effects that volatile has. Using them when reading and writing a variable is another way to make the changes visible to other threads:
class MyHouse {
boolean pizzaArrived = false;
void eatPizza() {
while (getPizzaArrived() == false) {}
System.out.println("That was delicious!");
}
synchronized boolean getPizzaArrived() {
return pizzaArrived;
}
synchronized void deliverPizza() {
pizzaArrived = true;
}
}
The effect of a print statement
System.out is a PrintStream object. The methods of PrintStream are synchronized like this:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
The synchronization prevents pizzaArrived being cached during the loop. Strictly speaking, both threads must synchronize on the same object to guarantee that changes to the variable are visible. (For example, calling println after setting pizzaArrived and calling it again before reading pizzaArrived would be correct.) If only one thread synchronizes on a particular object, the JVM is allowed to ignore it. In practice, the JVM is not smart enough to prove that other threads won't call println after setting pizzaArrived, so it assumes that they might. Therefore, it cannot cache the variable during the loop if you call System.out.println. That's why loops like this work when they have a print statement, although it is not a correct fix.
Using System.out is not the only way to cause this effect, but it is the one people discover most often, when they are trying to debug why their loop doesn't work!
The bigger problem
while (pizzaArrived == false) {} is a busy-wait loop. That's bad! While it waits, it hogs the CPU, which slows down other applications, and increases the power usage, temperature, and fan speed of the system. Ideally, we would like the loop thread to sleep while it waits, so it does not hog the CPU.
Here are some ways to do that:
Using wait/notify
A low-level solution is to use the 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 was delicious!");
}
void deliverPizza() {
synchronized (this) {
pizzaArrived = true;
this.notifyAll();
}
}
}
In this version of the code, the loop thread calls wait(), which puts the thread the sleep. It will not use any CPU cycles while sleeping. After the second thread sets the variable, it calls notifyAll() to wake up any/all threads which were waiting on that object. This is like having the pizza guy ring the doorbell, so you can sit down and rest while waiting, instead of standing awkwardly at the door.
When calling wait/notify on an object you must hold the synchronization lock of that object, which is what the above code does. You can use any object you like so long as both threads use the same object: here I used this (the instance of MyHouse). Usually, two threads would not be able to enter synchronized blocks of the same object simultaneously (which is part of the purpose of synchronization) but it works here because a thread temporarily releases the synchronization lock when it is inside the wait() method.
BlockingQueue
A BlockingQueue is used to implement producer-consumer queues. "Consumers" take items from the front of the queue, and "producers" push items on at the back. An example:
class MyHouse {
final BlockingQueue<Object> queue = new LinkedBlockingQueue<>();
void eatFood() throws InterruptedException {
// take next item from the queue (sleeps while waiting)
Object food = queue.take();
// and do something with it
System.out.println("Eating: " + food);
}
void deliverPizza() throws InterruptedException {
// in producer threads, we push items on to the queue.
// if there is space in the queue we can return immediately;
// the consumer thread(s) will get to it later
queue.put("A delicious pizza");
}
}
Note: The put and take methods of BlockingQueue can throw InterruptedExceptions, which are checked exceptions which must be handled. In the above code, for simplicity, the exceptions are rethrown. You might prefer to catch the exceptions in the methods and retry the put or take call to be sure it succeeds. Apart from that one point of ugliness, BlockingQueue is very easy to use.
No other synchronization is needed here because a BlockingQueue makes sure that everything threads did before putting items in the queue is visible to the threads taking those items out.
Executors
Executors are like ready-made BlockingQueues which execute tasks. Example:
// A "SingleThreadExecutor" has one work thread and an unlimited queue
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable eatPizza = () -> { System.out.println("Eating a delicious pizza"); };
Runnable cleanUp = () -> { System.out.println("Cleaning up the house"); };
// we submit tasks which will be executed on the work thread
executor.execute(eatPizza);
executor.execute(cleanUp);
// we continue immediately without needing to wait for the tasks to finish
For details see the doc for Executor, ExecutorService, and Executors.
Event handling
Looping while waiting for the user to click something in a UI is wrong. Instead, use the event handling features of the UI toolkit. In Swing, for example:
JLabel label = new JLabel();
JButton button = new JButton("Click me");
button.addActionListener((ActionEvent e) -> {
// This event listener is run when the button is clicked.
// We don't need to loop while waiting.
label.setText("Button was clicked");
});
Because the event handler runs on the event dispatch thread, doing long work in the event handler blocks other interaction with the UI until the work is finished. Slow operations can be started on a new thread, or dispatched to a waiting thread using one of the above techniques (wait/notify, a BlockingQueue, or Executor). You can also use a SwingWorker, which is designed exactly for this, and automatically supplies a background worker thread:
JLabel label = new JLabel();
JButton button = new JButton("Calculate answer");
// Add a click listener for the button
button.addActionListener((ActionEvent e) -> {
// Defines MyWorker as a SwingWorker whose result type is String:
class MyWorker extends SwingWorker<String,Void> {
#Override
public String doInBackground() throws Exception {
// This method is called on a background thread.
// You can do long work here without blocking the UI.
// This is just an example:
Thread.sleep(5000);
return "Answer is 42";
}
#Override
protected void done() {
// This method is called on the Swing thread once the work is done
String result;
try {
result = get();
} catch (Exception e) {
throw new RuntimeException(e);
}
label.setText(result); // will display "Answer is 42"
}
}
// Start the worker
new MyWorker().execute();
});
Timers
To perform periodic actions, you can use a java.util.Timer. It is easier to use than writing your own timing loop, and easier to start and stop. This demo prints the 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 its own background thread which is used to execute its scheduled TimerTasks. Naturally, the thread sleeps between tasks, so it does not hog the CPU.
In Swing code, there is also a javax.swing.Timer, which is similar, but it executes the listener on the Swing thread, so you can safely interact with Swing components without needing to 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 you are writing multithreaded code, it is worth exploring the classes in these packages to see what is available:
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
And also see the Concurrency section of the Java tutorials. Multithreading is complicated, but there is lots of help available!
In my code I have a loop that waits for some state to be changed from a different thread. The other thread works, but my loop never sees the changed value. It waits forever. However, when I put a System.out.println statement in the loop, it suddenly works! Why?
The following is an example of my code:
class MyHouse {
boolean pizzaArrived = false;
void eatPizza() {
while (pizzaArrived == false) {
//System.out.println("waiting");
}
System.out.println("That was delicious!");
}
void deliverPizza() {
pizzaArrived = true;
}
}
While the while loop is running, I call deliverPizza() from a different thread to set the pizzaArrived variable. But the loop only works when I uncomment the System.out.println("waiting"); statement. What's going on?
The JVM is allowed to assume that other threads do not change the pizzaArrived variable during the loop. In other words, it can hoist the pizzaArrived == false test outside the loop, optimizing this:
while (pizzaArrived == false) {}
into this:
if (pizzaArrived == false) while (true) {}
which is an infinite loop.
To ensure that changes made by one thread are visible to other threads you must always add some synchronization between the threads. The simplest way to do this is to make the shared variable volatile:
volatile boolean pizzaArrived = false;
Making a variable volatile guarantees that different threads will see the effects of each other's changes to it. This prevents the JVM from caching the value of pizzaArrived or hoisting the test outside the loop. Instead, it must read the value of the real variable every time.
(More formally, volatile creates a happens-before relationship between accesses to the variable. This means that all other work a thread did before delivering the pizza is also visible to the thread receiving the pizza, even if those other changes are not to volatile variables.)
Synchronized methods are used principally to implement mutual exclusion (preventing two things happening at the same time), but they also have all the same side-effects that volatile has. Using them when reading and writing a variable is another way to make the changes visible to other threads:
class MyHouse {
boolean pizzaArrived = false;
void eatPizza() {
while (getPizzaArrived() == false) {}
System.out.println("That was delicious!");
}
synchronized boolean getPizzaArrived() {
return pizzaArrived;
}
synchronized void deliverPizza() {
pizzaArrived = true;
}
}
The effect of a print statement
System.out is a PrintStream object. The methods of PrintStream are synchronized like this:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
The synchronization prevents pizzaArrived being cached during the loop. Strictly speaking, both threads must synchronize on the same object to guarantee that changes to the variable are visible. (For example, calling println after setting pizzaArrived and calling it again before reading pizzaArrived would be correct.) If only one thread synchronizes on a particular object, the JVM is allowed to ignore it. In practice, the JVM is not smart enough to prove that other threads won't call println after setting pizzaArrived, so it assumes that they might. Therefore, it cannot cache the variable during the loop if you call System.out.println. That's why loops like this work when they have a print statement, although it is not a correct fix.
Using System.out is not the only way to cause this effect, but it is the one people discover most often, when they are trying to debug why their loop doesn't work!
The bigger problem
while (pizzaArrived == false) {} is a busy-wait loop. That's bad! While it waits, it hogs the CPU, which slows down other applications, and increases the power usage, temperature, and fan speed of the system. Ideally, we would like the loop thread to sleep while it waits, so it does not hog the CPU.
Here are some ways to do that:
Using wait/notify
A low-level solution is to use the 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 was delicious!");
}
void deliverPizza() {
synchronized (this) {
pizzaArrived = true;
this.notifyAll();
}
}
}
In this version of the code, the loop thread calls wait(), which puts the thread the sleep. It will not use any CPU cycles while sleeping. After the second thread sets the variable, it calls notifyAll() to wake up any/all threads which were waiting on that object. This is like having the pizza guy ring the doorbell, so you can sit down and rest while waiting, instead of standing awkwardly at the door.
When calling wait/notify on an object you must hold the synchronization lock of that object, which is what the above code does. You can use any object you like so long as both threads use the same object: here I used this (the instance of MyHouse). Usually, two threads would not be able to enter synchronized blocks of the same object simultaneously (which is part of the purpose of synchronization) but it works here because a thread temporarily releases the synchronization lock when it is inside the wait() method.
BlockingQueue
A BlockingQueue is used to implement producer-consumer queues. "Consumers" take items from the front of the queue, and "producers" push items on at the back. An example:
class MyHouse {
final BlockingQueue<Object> queue = new LinkedBlockingQueue<>();
void eatFood() throws InterruptedException {
// take next item from the queue (sleeps while waiting)
Object food = queue.take();
// and do something with it
System.out.println("Eating: " + food);
}
void deliverPizza() throws InterruptedException {
// in producer threads, we push items on to the queue.
// if there is space in the queue we can return immediately;
// the consumer thread(s) will get to it later
queue.put("A delicious pizza");
}
}
Note: The put and take methods of BlockingQueue can throw InterruptedExceptions, which are checked exceptions which must be handled. In the above code, for simplicity, the exceptions are rethrown. You might prefer to catch the exceptions in the methods and retry the put or take call to be sure it succeeds. Apart from that one point of ugliness, BlockingQueue is very easy to use.
No other synchronization is needed here because a BlockingQueue makes sure that everything threads did before putting items in the queue is visible to the threads taking those items out.
Executors
Executors are like ready-made BlockingQueues which execute tasks. Example:
// A "SingleThreadExecutor" has one work thread and an unlimited queue
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable eatPizza = () -> { System.out.println("Eating a delicious pizza"); };
Runnable cleanUp = () -> { System.out.println("Cleaning up the house"); };
// we submit tasks which will be executed on the work thread
executor.execute(eatPizza);
executor.execute(cleanUp);
// we continue immediately without needing to wait for the tasks to finish
For details see the doc for Executor, ExecutorService, and Executors.
Event handling
Looping while waiting for the user to click something in a UI is wrong. Instead, use the event handling features of the UI toolkit. In Swing, for example:
JLabel label = new JLabel();
JButton button = new JButton("Click me");
button.addActionListener((ActionEvent e) -> {
// This event listener is run when the button is clicked.
// We don't need to loop while waiting.
label.setText("Button was clicked");
});
Because the event handler runs on the event dispatch thread, doing long work in the event handler blocks other interaction with the UI until the work is finished. Slow operations can be started on a new thread, or dispatched to a waiting thread using one of the above techniques (wait/notify, a BlockingQueue, or Executor). You can also use a SwingWorker, which is designed exactly for this, and automatically supplies a background worker thread:
JLabel label = new JLabel();
JButton button = new JButton("Calculate answer");
// Add a click listener for the button
button.addActionListener((ActionEvent e) -> {
// Defines MyWorker as a SwingWorker whose result type is String:
class MyWorker extends SwingWorker<String,Void> {
#Override
public String doInBackground() throws Exception {
// This method is called on a background thread.
// You can do long work here without blocking the UI.
// This is just an example:
Thread.sleep(5000);
return "Answer is 42";
}
#Override
protected void done() {
// This method is called on the Swing thread once the work is done
String result;
try {
result = get();
} catch (Exception e) {
throw new RuntimeException(e);
}
label.setText(result); // will display "Answer is 42"
}
}
// Start the worker
new MyWorker().execute();
});
Timers
To perform periodic actions, you can use a java.util.Timer. It is easier to use than writing your own timing loop, and easier to start and stop. This demo prints the 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 its own background thread which is used to execute its scheduled TimerTasks. Naturally, the thread sleeps between tasks, so it does not hog the CPU.
In Swing code, there is also a javax.swing.Timer, which is similar, but it executes the listener on the Swing thread, so you can safely interact with Swing components without needing to 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 you are writing multithreaded code, it is worth exploring the classes in these packages to see what is available:
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
And also see the Concurrency section of the Java tutorials. Multithreading is complicated, but there is lots of help available!
In my code I have a loop that waits for some state to be changed from a different thread. The other thread works, but my loop never sees the changed value. It waits forever. However, when I put a System.out.println statement in the loop, it suddenly works! Why?
The following is an example of my code:
class MyHouse {
boolean pizzaArrived = false;
void eatPizza() {
while (pizzaArrived == false) {
//System.out.println("waiting");
}
System.out.println("That was delicious!");
}
void deliverPizza() {
pizzaArrived = true;
}
}
While the while loop is running, I call deliverPizza() from a different thread to set the pizzaArrived variable. But the loop only works when I uncomment the System.out.println("waiting"); statement. What's going on?
The JVM is allowed to assume that other threads do not change the pizzaArrived variable during the loop. In other words, it can hoist the pizzaArrived == false test outside the loop, optimizing this:
while (pizzaArrived == false) {}
into this:
if (pizzaArrived == false) while (true) {}
which is an infinite loop.
To ensure that changes made by one thread are visible to other threads you must always add some synchronization between the threads. The simplest way to do this is to make the shared variable volatile:
volatile boolean pizzaArrived = false;
Making a variable volatile guarantees that different threads will see the effects of each other's changes to it. This prevents the JVM from caching the value of pizzaArrived or hoisting the test outside the loop. Instead, it must read the value of the real variable every time.
(More formally, volatile creates a happens-before relationship between accesses to the variable. This means that all other work a thread did before delivering the pizza is also visible to the thread receiving the pizza, even if those other changes are not to volatile variables.)
Synchronized methods are used principally to implement mutual exclusion (preventing two things happening at the same time), but they also have all the same side-effects that volatile has. Using them when reading and writing a variable is another way to make the changes visible to other threads:
class MyHouse {
boolean pizzaArrived = false;
void eatPizza() {
while (getPizzaArrived() == false) {}
System.out.println("That was delicious!");
}
synchronized boolean getPizzaArrived() {
return pizzaArrived;
}
synchronized void deliverPizza() {
pizzaArrived = true;
}
}
The effect of a print statement
System.out is a PrintStream object. The methods of PrintStream are synchronized like this:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
The synchronization prevents pizzaArrived being cached during the loop. Strictly speaking, both threads must synchronize on the same object to guarantee that changes to the variable are visible. (For example, calling println after setting pizzaArrived and calling it again before reading pizzaArrived would be correct.) If only one thread synchronizes on a particular object, the JVM is allowed to ignore it. In practice, the JVM is not smart enough to prove that other threads won't call println after setting pizzaArrived, so it assumes that they might. Therefore, it cannot cache the variable during the loop if you call System.out.println. That's why loops like this work when they have a print statement, although it is not a correct fix.
Using System.out is not the only way to cause this effect, but it is the one people discover most often, when they are trying to debug why their loop doesn't work!
The bigger problem
while (pizzaArrived == false) {} is a busy-wait loop. That's bad! While it waits, it hogs the CPU, which slows down other applications, and increases the power usage, temperature, and fan speed of the system. Ideally, we would like the loop thread to sleep while it waits, so it does not hog the CPU.
Here are some ways to do that:
Using wait/notify
A low-level solution is to use the 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 was delicious!");
}
void deliverPizza() {
synchronized (this) {
pizzaArrived = true;
this.notifyAll();
}
}
}
In this version of the code, the loop thread calls wait(), which puts the thread the sleep. It will not use any CPU cycles while sleeping. After the second thread sets the variable, it calls notifyAll() to wake up any/all threads which were waiting on that object. This is like having the pizza guy ring the doorbell, so you can sit down and rest while waiting, instead of standing awkwardly at the door.
When calling wait/notify on an object you must hold the synchronization lock of that object, which is what the above code does. You can use any object you like so long as both threads use the same object: here I used this (the instance of MyHouse). Usually, two threads would not be able to enter synchronized blocks of the same object simultaneously (which is part of the purpose of synchronization) but it works here because a thread temporarily releases the synchronization lock when it is inside the wait() method.
BlockingQueue
A BlockingQueue is used to implement producer-consumer queues. "Consumers" take items from the front of the queue, and "producers" push items on at the back. An example:
class MyHouse {
final BlockingQueue<Object> queue = new LinkedBlockingQueue<>();
void eatFood() throws InterruptedException {
// take next item from the queue (sleeps while waiting)
Object food = queue.take();
// and do something with it
System.out.println("Eating: " + food);
}
void deliverPizza() throws InterruptedException {
// in producer threads, we push items on to the queue.
// if there is space in the queue we can return immediately;
// the consumer thread(s) will get to it later
queue.put("A delicious pizza");
}
}
Note: The put and take methods of BlockingQueue can throw InterruptedExceptions, which are checked exceptions which must be handled. In the above code, for simplicity, the exceptions are rethrown. You might prefer to catch the exceptions in the methods and retry the put or take call to be sure it succeeds. Apart from that one point of ugliness, BlockingQueue is very easy to use.
No other synchronization is needed here because a BlockingQueue makes sure that everything threads did before putting items in the queue is visible to the threads taking those items out.
Executors
Executors are like ready-made BlockingQueues which execute tasks. Example:
// A "SingleThreadExecutor" has one work thread and an unlimited queue
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable eatPizza = () -> { System.out.println("Eating a delicious pizza"); };
Runnable cleanUp = () -> { System.out.println("Cleaning up the house"); };
// we submit tasks which will be executed on the work thread
executor.execute(eatPizza);
executor.execute(cleanUp);
// we continue immediately without needing to wait for the tasks to finish
For details see the doc for Executor, ExecutorService, and Executors.
Event handling
Looping while waiting for the user to click something in a UI is wrong. Instead, use the event handling features of the UI toolkit. In Swing, for example:
JLabel label = new JLabel();
JButton button = new JButton("Click me");
button.addActionListener((ActionEvent e) -> {
// This event listener is run when the button is clicked.
// We don't need to loop while waiting.
label.setText("Button was clicked");
});
Because the event handler runs on the event dispatch thread, doing long work in the event handler blocks other interaction with the UI until the work is finished. Slow operations can be started on a new thread, or dispatched to a waiting thread using one of the above techniques (wait/notify, a BlockingQueue, or Executor). You can also use a SwingWorker, which is designed exactly for this, and automatically supplies a background worker thread:
JLabel label = new JLabel();
JButton button = new JButton("Calculate answer");
// Add a click listener for the button
button.addActionListener((ActionEvent e) -> {
// Defines MyWorker as a SwingWorker whose result type is String:
class MyWorker extends SwingWorker<String,Void> {
#Override
public String doInBackground() throws Exception {
// This method is called on a background thread.
// You can do long work here without blocking the UI.
// This is just an example:
Thread.sleep(5000);
return "Answer is 42";
}
#Override
protected void done() {
// This method is called on the Swing thread once the work is done
String result;
try {
result = get();
} catch (Exception e) {
throw new RuntimeException(e);
}
label.setText(result); // will display "Answer is 42"
}
}
// Start the worker
new MyWorker().execute();
});
Timers
To perform periodic actions, you can use a java.util.Timer. It is easier to use than writing your own timing loop, and easier to start and stop. This demo prints the 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 its own background thread which is used to execute its scheduled TimerTasks. Naturally, the thread sleeps between tasks, so it does not hog the CPU.
In Swing code, there is also a javax.swing.Timer, which is similar, but it executes the listener on the Swing thread, so you can safely interact with Swing components without needing to 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 you are writing multithreaded code, it is worth exploring the classes in these packages to see what is available:
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
And also see the Concurrency section of the Java tutorials. Multithreading is complicated, but there is lots of help available!
In my code I have a loop that waits for some state to be changed from a different thread. The other thread works, but my loop never sees the changed value. It waits forever. However, when I put a System.out.println statement in the loop, it suddenly works! Why?
The following is an example of my code:
class MyHouse {
boolean pizzaArrived = false;
void eatPizza() {
while (pizzaArrived == false) {
//System.out.println("waiting");
}
System.out.println("That was delicious!");
}
void deliverPizza() {
pizzaArrived = true;
}
}
While the while loop is running, I call deliverPizza() from a different thread to set the pizzaArrived variable. But the loop only works when I uncomment the System.out.println("waiting"); statement. What's going on?
The JVM is allowed to assume that other threads do not change the pizzaArrived variable during the loop. In other words, it can hoist the pizzaArrived == false test outside the loop, optimizing this:
while (pizzaArrived == false) {}
into this:
if (pizzaArrived == false) while (true) {}
which is an infinite loop.
To ensure that changes made by one thread are visible to other threads you must always add some synchronization between the threads. The simplest way to do this is to make the shared variable volatile:
volatile boolean pizzaArrived = false;
Making a variable volatile guarantees that different threads will see the effects of each other's changes to it. This prevents the JVM from caching the value of pizzaArrived or hoisting the test outside the loop. Instead, it must read the value of the real variable every time.
(More formally, volatile creates a happens-before relationship between accesses to the variable. This means that all other work a thread did before delivering the pizza is also visible to the thread receiving the pizza, even if those other changes are not to volatile variables.)
Synchronized methods are used principally to implement mutual exclusion (preventing two things happening at the same time), but they also have all the same side-effects that volatile has. Using them when reading and writing a variable is another way to make the changes visible to other threads:
class MyHouse {
boolean pizzaArrived = false;
void eatPizza() {
while (getPizzaArrived() == false) {}
System.out.println("That was delicious!");
}
synchronized boolean getPizzaArrived() {
return pizzaArrived;
}
synchronized void deliverPizza() {
pizzaArrived = true;
}
}
The effect of a print statement
System.out is a PrintStream object. The methods of PrintStream are synchronized like this:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
The synchronization prevents pizzaArrived being cached during the loop. Strictly speaking, both threads must synchronize on the same object to guarantee that changes to the variable are visible. (For example, calling println after setting pizzaArrived and calling it again before reading pizzaArrived would be correct.) If only one thread synchronizes on a particular object, the JVM is allowed to ignore it. In practice, the JVM is not smart enough to prove that other threads won't call println after setting pizzaArrived, so it assumes that they might. Therefore, it cannot cache the variable during the loop if you call System.out.println. That's why loops like this work when they have a print statement, although it is not a correct fix.
Using System.out is not the only way to cause this effect, but it is the one people discover most often, when they are trying to debug why their loop doesn't work!
The bigger problem
while (pizzaArrived == false) {} is a busy-wait loop. That's bad! While it waits, it hogs the CPU, which slows down other applications, and increases the power usage, temperature, and fan speed of the system. Ideally, we would like the loop thread to sleep while it waits, so it does not hog the CPU.
Here are some ways to do that:
Using wait/notify
A low-level solution is to use the 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 was delicious!");
}
void deliverPizza() {
synchronized (this) {
pizzaArrived = true;
this.notifyAll();
}
}
}
In this version of the code, the loop thread calls wait(), which puts the thread the sleep. It will not use any CPU cycles while sleeping. After the second thread sets the variable, it calls notifyAll() to wake up any/all threads which were waiting on that object. This is like having the pizza guy ring the doorbell, so you can sit down and rest while waiting, instead of standing awkwardly at the door.
When calling wait/notify on an object you must hold the synchronization lock of that object, which is what the above code does. You can use any object you like so long as both threads use the same object: here I used this (the instance of MyHouse). Usually, two threads would not be able to enter synchronized blocks of the same object simultaneously (which is part of the purpose of synchronization) but it works here because a thread temporarily releases the synchronization lock when it is inside the wait() method.
BlockingQueue
A BlockingQueue is used to implement producer-consumer queues. "Consumers" take items from the front of the queue, and "producers" push items on at the back. An example:
class MyHouse {
final BlockingQueue<Object> queue = new LinkedBlockingQueue<>();
void eatFood() throws InterruptedException {
// take next item from the queue (sleeps while waiting)
Object food = queue.take();
// and do something with it
System.out.println("Eating: " + food);
}
void deliverPizza() throws InterruptedException {
// in producer threads, we push items on to the queue.
// if there is space in the queue we can return immediately;
// the consumer thread(s) will get to it later
queue.put("A delicious pizza");
}
}
Note: The put and take methods of BlockingQueue can throw InterruptedExceptions, which are checked exceptions which must be handled. In the above code, for simplicity, the exceptions are rethrown. You might prefer to catch the exceptions in the methods and retry the put or take call to be sure it succeeds. Apart from that one point of ugliness, BlockingQueue is very easy to use.
No other synchronization is needed here because a BlockingQueue makes sure that everything threads did before putting items in the queue is visible to the threads taking those items out.
Executors
Executors are like ready-made BlockingQueues which execute tasks. Example:
// A "SingleThreadExecutor" has one work thread and an unlimited queue
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable eatPizza = () -> { System.out.println("Eating a delicious pizza"); };
Runnable cleanUp = () -> { System.out.println("Cleaning up the house"); };
// we submit tasks which will be executed on the work thread
executor.execute(eatPizza);
executor.execute(cleanUp);
// we continue immediately without needing to wait for the tasks to finish
For details see the doc for Executor, ExecutorService, and Executors.
Event handling
Looping while waiting for the user to click something in a UI is wrong. Instead, use the event handling features of the UI toolkit. In Swing, for example:
JLabel label = new JLabel();
JButton button = new JButton("Click me");
button.addActionListener((ActionEvent e) -> {
// This event listener is run when the button is clicked.
// We don't need to loop while waiting.
label.setText("Button was clicked");
});
Because the event handler runs on the event dispatch thread, doing long work in the event handler blocks other interaction with the UI until the work is finished. Slow operations can be started on a new thread, or dispatched to a waiting thread using one of the above techniques (wait/notify, a BlockingQueue, or Executor). You can also use a SwingWorker, which is designed exactly for this, and automatically supplies a background worker thread:
JLabel label = new JLabel();
JButton button = new JButton("Calculate answer");
// Add a click listener for the button
button.addActionListener((ActionEvent e) -> {
// Defines MyWorker as a SwingWorker whose result type is String:
class MyWorker extends SwingWorker<String,Void> {
#Override
public String doInBackground() throws Exception {
// This method is called on a background thread.
// You can do long work here without blocking the UI.
// This is just an example:
Thread.sleep(5000);
return "Answer is 42";
}
#Override
protected void done() {
// This method is called on the Swing thread once the work is done
String result;
try {
result = get();
} catch (Exception e) {
throw new RuntimeException(e);
}
label.setText(result); // will display "Answer is 42"
}
}
// Start the worker
new MyWorker().execute();
});
Timers
To perform periodic actions, you can use a java.util.Timer. It is easier to use than writing your own timing loop, and easier to start and stop. This demo prints the 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 its own background thread which is used to execute its scheduled TimerTasks. Naturally, the thread sleeps between tasks, so it does not hog the CPU.
In Swing code, there is also a javax.swing.Timer, which is similar, but it executes the listener on the Swing thread, so you can safely interact with Swing components without needing to 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 you are writing multithreaded code, it is worth exploring the classes in these packages to see what is available:
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
And also see the Concurrency section of the Java tutorials. Multithreading is complicated, but there is lots of help available!
In my code I have a loop that waits for some state to be changed from a different thread. The other thread works, but my loop never sees the changed value. It waits forever. However, when I put a System.out.println statement in the loop, it suddenly works! Why?
The following is an example of my code:
class MyHouse {
boolean pizzaArrived = false;
void eatPizza() {
while (pizzaArrived == false) {
//System.out.println("waiting");
}
System.out.println("That was delicious!");
}
void deliverPizza() {
pizzaArrived = true;
}
}
While the while loop is running, I call deliverPizza() from a different thread to set the pizzaArrived variable. But the loop only works when I uncomment the System.out.println("waiting"); statement. What's going on?
The JVM is allowed to assume that other threads do not change the pizzaArrived variable during the loop. In other words, it can hoist the pizzaArrived == false test outside the loop, optimizing this:
while (pizzaArrived == false) {}
into this:
if (pizzaArrived == false) while (true) {}
which is an infinite loop.
To ensure that changes made by one thread are visible to other threads you must always add some synchronization between the threads. The simplest way to do this is to make the shared variable volatile:
volatile boolean pizzaArrived = false;
Making a variable volatile guarantees that different threads will see the effects of each other's changes to it. This prevents the JVM from caching the value of pizzaArrived or hoisting the test outside the loop. Instead, it must read the value of the real variable every time.
(More formally, volatile creates a happens-before relationship between accesses to the variable. This means that all other work a thread did before delivering the pizza is also visible to the thread receiving the pizza, even if those other changes are not to volatile variables.)
Synchronized methods are used principally to implement mutual exclusion (preventing two things happening at the same time), but they also have all the same side-effects that volatile has. Using them when reading and writing a variable is another way to make the changes visible to other threads:
class MyHouse {
boolean pizzaArrived = false;
void eatPizza() {
while (getPizzaArrived() == false) {}
System.out.println("That was delicious!");
}
synchronized boolean getPizzaArrived() {
return pizzaArrived;
}
synchronized void deliverPizza() {
pizzaArrived = true;
}
}
The effect of a print statement
System.out is a PrintStream object. The methods of PrintStream are synchronized like this:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
The synchronization prevents pizzaArrived being cached during the loop. Strictly speaking, both threads must synchronize on the same object to guarantee that changes to the variable are visible. (For example, calling println after setting pizzaArrived and calling it again before reading pizzaArrived would be correct.) If only one thread synchronizes on a particular object, the JVM is allowed to ignore it. In practice, the JVM is not smart enough to prove that other threads won't call println after setting pizzaArrived, so it assumes that they might. Therefore, it cannot cache the variable during the loop if you call System.out.println. That's why loops like this work when they have a print statement, although it is not a correct fix.
Using System.out is not the only way to cause this effect, but it is the one people discover most often, when they are trying to debug why their loop doesn't work!
The bigger problem
while (pizzaArrived == false) {} is a busy-wait loop. That's bad! While it waits, it hogs the CPU, which slows down other applications, and increases the power usage, temperature, and fan speed of the system. Ideally, we would like the loop thread to sleep while it waits, so it does not hog the CPU.
Here are some ways to do that:
Using wait/notify
A low-level solution is to use the 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 was delicious!");
}
void deliverPizza() {
synchronized (this) {
pizzaArrived = true;
this.notifyAll();
}
}
}
In this version of the code, the loop thread calls wait(), which puts the thread the sleep. It will not use any CPU cycles while sleeping. After the second thread sets the variable, it calls notifyAll() to wake up any/all threads which were waiting on that object. This is like having the pizza guy ring the doorbell, so you can sit down and rest while waiting, instead of standing awkwardly at the door.
When calling wait/notify on an object you must hold the synchronization lock of that object, which is what the above code does. You can use any object you like so long as both threads use the same object: here I used this (the instance of MyHouse). Usually, two threads would not be able to enter synchronized blocks of the same object simultaneously (which is part of the purpose of synchronization) but it works here because a thread temporarily releases the synchronization lock when it is inside the wait() method.
BlockingQueue
A BlockingQueue is used to implement producer-consumer queues. "Consumers" take items from the front of the queue, and "producers" push items on at the back. An example:
class MyHouse {
final BlockingQueue<Object> queue = new LinkedBlockingQueue<>();
void eatFood() throws InterruptedException {
// take next item from the queue (sleeps while waiting)
Object food = queue.take();
// and do something with it
System.out.println("Eating: " + food);
}
void deliverPizza() throws InterruptedException {
// in producer threads, we push items on to the queue.
// if there is space in the queue we can return immediately;
// the consumer thread(s) will get to it later
queue.put("A delicious pizza");
}
}
Note: The put and take methods of BlockingQueue can throw InterruptedExceptions, which are checked exceptions which must be handled. In the above code, for simplicity, the exceptions are rethrown. You might prefer to catch the exceptions in the methods and retry the put or take call to be sure it succeeds. Apart from that one point of ugliness, BlockingQueue is very easy to use.
No other synchronization is needed here because a BlockingQueue makes sure that everything threads did before putting items in the queue is visible to the threads taking those items out.
Executors
Executors are like ready-made BlockingQueues which execute tasks. Example:
// A "SingleThreadExecutor" has one work thread and an unlimited queue
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable eatPizza = () -> { System.out.println("Eating a delicious pizza"); };
Runnable cleanUp = () -> { System.out.println("Cleaning up the house"); };
// we submit tasks which will be executed on the work thread
executor.execute(eatPizza);
executor.execute(cleanUp);
// we continue immediately without needing to wait for the tasks to finish
For details see the doc for Executor, ExecutorService, and Executors.
Event handling
Looping while waiting for the user to click something in a UI is wrong. Instead, use the event handling features of the UI toolkit. In Swing, for example:
JLabel label = new JLabel();
JButton button = new JButton("Click me");
button.addActionListener((ActionEvent e) -> {
// This event listener is run when the button is clicked.
// We don't need to loop while waiting.
label.setText("Button was clicked");
});
Because the event handler runs on the event dispatch thread, doing long work in the event handler blocks other interaction with the UI until the work is finished. Slow operations can be started on a new thread, or dispatched to a waiting thread using one of the above techniques (wait/notify, a BlockingQueue, or Executor). You can also use a SwingWorker, which is designed exactly for this, and automatically supplies a background worker thread:
JLabel label = new JLabel();
JButton button = new JButton("Calculate answer");
// Add a click listener for the button
button.addActionListener((ActionEvent e) -> {
// Defines MyWorker as a SwingWorker whose result type is String:
class MyWorker extends SwingWorker<String,Void> {
#Override
public String doInBackground() throws Exception {
// This method is called on a background thread.
// You can do long work here without blocking the UI.
// This is just an example:
Thread.sleep(5000);
return "Answer is 42";
}
#Override
protected void done() {
// This method is called on the Swing thread once the work is done
String result;
try {
result = get();
} catch (Exception e) {
throw new RuntimeException(e);
}
label.setText(result); // will display "Answer is 42"
}
}
// Start the worker
new MyWorker().execute();
});
Timers
To perform periodic actions, you can use a java.util.Timer. It is easier to use than writing your own timing loop, and easier to start and stop. This demo prints the 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 its own background thread which is used to execute its scheduled TimerTasks. Naturally, the thread sleeps between tasks, so it does not hog the CPU.
In Swing code, there is also a javax.swing.Timer, which is similar, but it executes the listener on the Swing thread, so you can safely interact with Swing components without needing to 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 you are writing multithreaded code, it is worth exploring the classes in these packages to see what is available:
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
And also see the Concurrency section of the Java tutorials. Multithreading is complicated, but there is lots of help available!