@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)