Josh Juneau 2017
Josh Juneau , Java 9 Recipes , 10.1007/978-1-4842-1976-8_10
10. Concurrency
Josh Juneau 1
(1) Hinckley, Illinois, USA
Concurrency is one of the toughest topics to handle in modern computer programming; understanding concurrency requires the capacity of thinking abstractly, and debugging concurrent problems is like trying to pilot an airplane by dead reckoning. Even so, with modern releases of Java, it has become easier (and more accessible) to write bug-free concurrent code.
Concurrency is the ability of a program to execute different (or the same) instructions at the same time. A program that is said to be concurrent has the ability to be split up and run on multiple CPUs. By making concurrent programs, you take advantage of todays multicore CPUs. You can even see benefit on single-core CPUs that are I/O intensive.
In this chapter, we present the most common need for concurrency tasksfrom running a background task to splitting a computation into work units. Throughout the chapter, you will find the most up-to-date recipes for accomplishing concurrency in Java.
10-1. Starting a Background Task
Problem
You have a task that needs to run outside of your main thread.
Solution
Create a class implementation that includes the task that needs to be run in a different thread. Implement a Runnable interface in the task implementation class and start a new Thread . In the following example, a counter is used to simulate activity, as a separate task is run in the background.
Note
The code in this example could be refactored to utilize method references (see Chapter ), rather than creating an inner class for the new Thread implementation. However for clarity, the anonymous inner class has been shown.
private void someMethod() {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
doSomethingInBackground();
}
},"Background Thread");
System.out.println("Start");
backgroundThread.start();
for (int i= 0;i < 10;i++) {
System.out.println(Thread.currentThread().getName()+": is counting "+i);
}
System.out.println("Done");
}
private void doSomethingInBackground() {
System.out.println(Thread.currentThread().getName()+
": is Running in the background");
}
If the code is executed more than once, the output should be different from time to time. The background thread will execute separately, so its message is printed at a different time across each run.
The same code for creating the background thread can be written as follows if youre using lambda expressions:
Thread backgroundThread = new Thread(this::doSomethingInBackground, "Background Thread");
How It Works
The Thread class allows executing code in a new thread (path of execution), distinct from the current thread. The Thread constructor requires as a parameter a class that implements the Runnable interface. The Runnable interface requires the implementation of only one method: public void run(). Hence, it is a functional interface, which facilitates the use of lambda expressions. When the Thread.start() method is invoked, it will in turn create the new thread and invoke the run() method of the Runnable .
Within the JVM are two types of threads: User and Daemon. User threads keep executing until their run() method completes, whereas Daemon threads can be terminated if the application needs to exit. An application exits if there are only Daemon threads running in the JVM. When you start to create multithreaded applications, you must be aware of these differences and understand when to use each type of thread.
Usually, Daemon threads will have a Runnable interface that doesnt complete; for example a while (true) loop. This allows these threads to periodically check or perform a certain condition throughout the life of the program, and be discarded when the program is finished executing. In contrast, User threads, while alive, will execute and prevent the program from terminating. If you happen to have a program that is not closing and/or exiting when expected, you might want to check the threads that are actively running.
To set a thread as a Daemon thread, use thread.setDaemon(true) before calling the thread.start() method. By default, Thread instances are created as User thread types.
Note
This recipe shows the simplest way to create and execute a new thread. The new thread created is a User thread, which means that the application will not exit until both the main thread and the background thread are done executing.
10-2. Updating (and Iterating) a Map
Problem
You need to update a Map object from multiple threads, and you want to make sure that the update doesnt break the contents of the Map object and that the Map object is always in a consistent state. You also want to traverse (look at) the content of the Map object while other threads are updating the Map object.
Solution
Use a ConcurrentMap to update Map entries. The following example creates 1,000 threads. Each thread then tries to modify the Map at the same time. The main thread waits for a second, and then proceeds to iterate through the Map (even when the other threads are still modifying the Map ):
Set updateThreads = new HashSet<>();
private void startProcess() {
ConcurrentMap concurrentMap = new ConcurrentHashMap<>();
for (int i =0;i < 1000;i++) {
startUpdateThread(i, concurrentMap);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
concurrentMap.entrySet().stream().forEach((entry) -> {
System.out.println("Key :"+entry.getKey()+" Value:"+entry.getValue());
});
updateThreads.stream().forEach((thread) -> {
thread.interrupt();
});
}
Random random = new Random();
private void startUpdateThread(int i, final ConcurrentMap concurrentMap) {
Thread thread = new Thread(() -> {
while (!Thread.interrupted()) {
int randomInt = random.nextInt(20);
concurrentMap.put(randomInt, UUID.randomUUID().toString());
}
});
thread.setName("Update Thread "+i);
updateThreads.add(thread);
thread.start();
}
How It Works
For performing work on a hash table in a concurrent manner, ConcurrentHashMap allows multiple threads to modify the hash table concurrently and safely. ConcurrentHashMap is a hash table supporting full concurrency for retrievals, and adjustable expected concurrency for updates. In the example, 1,000 threads make modifications to the Map over a short period of time. The ConcurrentHashMap iterator , as well as streams that are generated on a ConcurrentHashMap, allows safe iteration over its contents. When using the ConcurrentMap s iterator, you do not have to worry about locking the contents of the ConcurrentMap while iterating over it (and it doesnt throw ConcurrentModificationExceptions ).
For a complete list of the newly added methods, refer to the online documentation at http://docs.oracle.com/javase/9/docs/api/java/util/concurrent/ConcurrentHashMap.html .
Next page