KEMBAR78
Java APIs- The missing manual (concurrency) | PDF
@HendrikEbbers
Karakun DevHub_
dev.karakun.com
@HendrikEbbers
JavaAPIsThe missing manual
Karakun DevHub_
@HendrikEbbersdev.karakun.com
About me
• Karakun Co-Founder
• Lead of JUG Dortmund
• JSR EG member
• JavaOne Rockstar, Java Champion
• JavaLand Programm Chair
Karakun DevHub_
@HendrikEbbersdev.karakun.com
About me
@HendrikEbbers
Executors
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• Whenever code should be executed in parallel it gets
much harder…





Let's write a simple http server in Java
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
public class HttpServer {
public static void main(String… args) {
while(true) {
HttpRequest request = getNextRequest();
request.sendResponse("Hello");
}
}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
public class HttpServer {
public static void main(String… args) {
while(true) {
HttpRequest request = getNextRequest();
request.sendResponse("Hello");
}
}
}
returns the nextincoming request
sends response to client content of the response
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Headline
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• Let's test this with several users
• We can simulate this in Java



• What will happen?
URL url = new URL("http://localhost:8080/hello");

System.out.println(IOUtils.toString(url.openStream()));
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Headline
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• We can't use a single threaded approach here
• The handling must work in parallel.
• Let's have a look on a solution that is based on the
Thread class
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
public static void main(String… args) {
while(true) {
final HttpRequest request = getNextRequest();
Runnable r = () -> {
request.sendResponse("Hello");
}
new Thread(r).start();
}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Headline
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Only the "Application" module knows all
implementations since it's the only module that
depends on all other modules
That was easy, bro!
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• The server is working much better
• But we lost control:
• How many threads are running?
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
But the biggest question is:
How many threads
can we run in Java?
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• Using the old school Thread class seams to be a
bad idea
• Let's try to use some modern Java concurrency
classes
the java.util.concurrent package
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• To execute tasks in a background thread Java
provides the Executor interface
• Instances can be created by using the factory
methods of the Executors class
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
Executor myExecutor = Executors.newSingleThreadExecutor();
Runnable r = () -> doSomeAction();
myExecutor.execute(r);
Creates an executer
Runnable will be
executed in a thread.
method call do not block
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
Executor myExecutor = Executors.newSingleThreadExecutor();
Executor myExecutor = Executors.newFixedThreadPool(5);
Wraps one thread
Wraps 5 threads
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• Executor instances can easily be reused.
• No need to create a new one for each call.
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• Different behavior based on the given Executor
for(int i = 0; i < 100; i++) {
final Runnable r = () -> {
sleep(2_000);
print("Moin");
}
executor.execute(r);
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
Let me introduce you the
CELEBRITY
EXECUTOR
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• No specific thread count defined
• Threads will be created and destroyed based on the
work load
• Threads will be reused
Executor myExecutor = Executors.newCachedThreadPool();
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
public static void main(String… args) {
final Executor executor = Executors.newCachedThreadPool();
while(true) {
final HttpRequest request = getNextRequest();
final Runnable r = () -> {
request.sendResponse("Hello");
}
executor.execute(r);
}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• We said that we do not want to use the Thread
class anymore.
• All types are based on threads…
• And we can configure them…
• So let's use Thread again
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
• A custom thread factory can be used to create an
executor
• Thread factory can configure the internal threads
ThreadFactory tf = . . .
Executor myExecutor = Executors.newCachedThreadPool(tf);
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
public class MyThreadFactory implements ThreadFactory {



final AtomicLong counter = new AtomicLong(0);



@Override

public Thread newThread(Runnable r) {

final Thread thread = new Thread();

thread.setName("My private thread " + counter.incrementAndGet());

thread.setUncaughtExceptionHandler((t, e) -> {
System.out.println("BOOOOOOM!");
});

return thread;

}

}

Karakun DevHub_
@HendrikEbbersdev.karakun.com
Executors
But wait!
the factory returns
ExecutorService
and not
Executor
instances
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Headline
• ExecutorService provides a lot of
new functionality.
• We will handle this in a separate
chapter
@HendrikEbbers
Sync
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Synchronization
• Let's start this chapter with a simple class
• Only a wrapper for a list
• What could go wrong…
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Synchronization
public class Holder {
private final List<String> data = new ArrayList<>();
public void add(String v) {this.data.add(v);}
public void remove(String v) {this.data.remove(v);}
public void forEach(Consumer c) { data.forEach(c);}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Synchronization
add("A");
add("B");
add("C");
add("D");
add("E");
add("F");
add("G");
add("H");
remove("1");
remove("2");
remove("3");
remove("4");
remove("5");
remove("6");
remove("7");
remove("8");
forEach(. . .);
forEach(. . .);
forEach(. . .);
forEach(. . .);
forEach(. . .);
forEach(. . .);
forEach(. . .);
forEach(. . .);
User A User B User C
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Synchronization
add("A");
add("B");
add("C");
add("D");
add("E");
add("F");
add("G");
add("H");
remove("1");
remove("2");
remove("3");
remove("4");
remove("5");
remove("6");
remove("7");
remove("8");
forEach(. . .);
forEach(. . .);
forEach(. . .);
forEach(. . .);
forEach(. . .);
forEach(. . .);
forEach(. . .);
forEach(. . .);
User A User B User C
Thread Thread Thread
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Synchronization
ConcurrentModificationException
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Synchronization
ConcurrentModificationException
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Synchronization
• Java provides a keyword that defines synchronized
access for such use cases
Do you know The Keyword?
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
public class Holder {
public synchronized void add(String v) {. . .}
public synchronized void remove(String v) {. . .}
public synchronized void forEach(Consumer c) {. . .}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
add("A");
add("B");
add("C");
remove("1");
remove("2");
remove("3");
forEach(. . .);
forEach(. . .);
wait
wait
wait
wait
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
• The synchronized keyword provides thread
synchronization
• Internally Java provides a monitor lock
• Each monitor is bound to an object
• Java provides several ways how the
synchronized keyword can be used
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
public class Holder {
public synchronized void add(String v) {. . .}
}
Lock is based on this (the
object instance)
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
final Holder a = new Holder();
final Holder b = new Holder();
doSomeParallelStuff(a, b);
No synchronization between
instance a and B
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
public class Holder {
public static synchronized void myMethod() {. . .}
}
Lock is based on the
Holder class
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
• The synchronized keyword provides reentrant
functionality
• A lock is always bound to the current thread
• A thread can acquire the same lock several times
• Helps to avoid deadlocks
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
public class Holder {
public synchronized void myMethod() {
myOtherMethod();
}
public synchronized void myOtherMethod() {
//TODO: add code
}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
• Using synchronized can create some problems
• Think about using synchronized in a base class and
derived classes
• Ends in implicit synchronization dependencies
• Can end in deadlocks
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
public class Holder {
private final List<String> l = new ArrayList();
public synchronized void add(String v) {l.add(v);}
public synchronized void remove(String v) {l.remove(v);}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
public class MyHolder extends Holder {
private final List<Integer> l = new ArrayList();
public synchronized void addInt(int v) {l.add(v);}
}
It is not possible to call
the 2 add methods in
parallel
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
public class MyHolder extends Holder {
private final List<Integer> l = new ArrayList();
public synchronized void run() {
while(true) {
sleep(1000);
printTime();
}
}
}
Once this is called all
synchronised methods form
Holder class are useless…
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
• Try to use synchronized by specifying a private
monitor object
• To do so synchronized can be used as a block
statement
• Derived classes can not access the monitor object
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
public class Holder {
private final Object monitor = new Object();
public void myMethod() {
synchronized(monitor) {
//TODO: add code
}
}
}
Internal Code of the block
is synchronized.
Lock is based on monitor
object / instance
Karakun DevHub_
@HendrikEbbersdev.karakun.com
synchronized
• Since synchronized is a Java keyword it is
compiled in specified byte code instructions
• Instructions called MonitorEnter and
MonitorExit
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
• Since Java 1.5 we can use a Java API for
synchronization
• Against the synchronized keyword we have all the
benefits of an API
• See java.util.concurrent.locks.Lock
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
public class Holder {
private final List<String> listA;
private final Lock listLockA;
private final List<String> listB;
private final Lock listLockB;
}
Just define a lock instance
for each Monitor that
should be synchronized
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
• Use ReentrantLock as concrete type
private final Lock listLockA = new ReentrantLock();
Same behavior as synchronized
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
• A Lock controls access to a shared resource by
multiple threads
• The most important methods of a Lock are lock()
and unlock()
• This method defines exclusive access to a resource
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
• By calling lock() the resource will be locked by the
current thread
• By calling unlock() the current thread can finish
the exclusive locking of the resource
• Calling lock() will block the current thread until no
other thread has an exclusive lock
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
public void addToA(final String v) {
listLockA.lock();
getListA().add(v);
listLockA.unlock();
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Only the "Application" module knows all
implementations since it's the only module that
depends on all other modules
That was easy, bro!
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
public void addToA(String v) {
listLockA.lock();
getListA().add(v);
listLockA.unlock();
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
public void addToA(String v) {
listLockA.lock();
getListA().add(v);
listLockA.unlock();
}
Exception
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
• NEVER, NEVER, NEVER USE LOCK WITHOUT
TRY-FINAL
lock.lock();
try {
//access resource
} finally {
lock.unlock();
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
public void addToA(String v) {
listLockA.lock();
try {
getListA().add(v);
} finally {
listLockA.unlock();
}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Lock
• Even more complexe use cases are supported by
the Lock API.
• Use tryLock() to not block threads.
• Create a Condition for monitor functionality.
Will be handled in a
separate topic
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Other solutions
• By the way the complete problem could be handled
in a different way:

• Java provides some special collection types to get
rid of ConcurrentModificationException:
Will be handled in a
separate topic
private final List<String> listA = new ArrayList<>();
List<String> listA = new CopyOneWriteArrayList<>();
@HendrikEbbers
• Check our website for developers

• Subscribe to our newsletter

• Join us
dev.karakun.com | @HendrikEbbers
Karakun DevHub_
dev.karakun.com
https://dev.karakun.com
https://dev.karakun.com/subscribe/
https://dev.karakun.com/you-at-karakun/

Java APIs- The missing manual (concurrency)