Java threads Tutorial or Java 8 concurrency Tutorial or Java 8 Threads or Multithreading tutorial in Java
Java concurrency Topics or Key Concurrency Topics we will discuss:
- Creating a thread using Thread class and Runnable Interface.
- Creating a thread using Thread class and Runnable Interface using Java 8 Lambda Syntax.
- Running multiple threads using "Executor service" and kick start them using
eg: ExecutorService executor = Executors.newFixedThreadPool(2);
- Callable and Future interfaces: Want your threads run method to return results ? In case you want to return results from run method use Callable interface and put your code in call() method (similar to run() but can return a result). Similar to Runnable interface's run method, the Callable interface has has call() which returns result wrapped in Future<?> interface
- Using ExecutorService.invokeall() to submit multiple Threads that implement Callable<...> interface
- Using "Executors.newWorkStealingPool()" - where in the number of threads may grow or Shrink.A work-stealing pool makes no guarantees about the order in which submitted tasks are executed and using "executor.invokeAny(callableTasks);" - Instead of returning future objects this method blocks until the first callable terminates and returns the result of that callable.
- Scheduling when to run a thread: Want to run a callable at a specified time here are some options.
- ScheduledFuture<Employee> schedFuture0 = executor.schedule(task0, 3, TimeUnit.SECONDS); //Execute after 3 seconds
- ScheduledFuture<Employee> schedFuture1 = executor.schedule(task1, 10, TimeUnit.SECONDS); //Execute after 10 seconds
- ScheduledFuture<Employee> schedFuture2 = executor.schedule(task2, 3, TimeUnit.SECONDS); //Execute after 3 seconds.
- Want to run a thread with fixed delay ?
- executor.scheduleWithFixedDelay(task, initialDelay, period, TimeUnit.SECONDS);
- executor.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS);
- Synchronized block vs Synchronized method.
- Calling of wait(), notify(), notifyAll() in a synchronized context
- wait() method tells the current thread (thread which is executing code inside a synchronized method or lock) to give up monitor and go to waiting state.
- notify() method Wakes up a single thread that is waiting on this object's monitor.
- notifyAll() method wakes up all the threads that called wait( ) on the same object.
- Volatile - why a variable should be marked volatile (To tell thread not to cache and instead re-read the value each time.)
CountDownLatch is used to start a series of threads and then wait until all of them are complete (or until they call countDown() a given number of times CountDownLatch cannot be reused after meeting the final count.
(Just opposite of Semaphore discussed further. In case you want to reuse countdownlatch try "CyclicBarrier")
A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.
CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other.
The barrier is called cyclic because it can be re-used after the waiting threads are released.
( Cyclicbarrier is similar to countdownlatch except CyclicBarrier can be re used.)
- Count down Latch VS CyclicBarriers:
- CountDownLatch can not be reused after meeting the final count.
- CountDownLatch can not be used to wait for Parallel Threads to finish.
- CyclicBarrier can be reset thus reused.
- CyclicBarrier can be used to wait for Parallel Threads to finish.
A re-entrant mutual exclusion lock with the same basic behavior and semantics as the implicit monitor lock accessed using
a synchronized methods or statements, but with extended capabilities.
Locks support various methods for finer grained lock control thus are more expressive than implicit monitors.
- RentrantLock with condition:
The Condition interface factors out the java.lang.Object monitor methods (wait(), notify(), and notifyAll()) into distinct objects to give the effect of having multiple wait-sets per object,by combining them with the use of arbitrary Lock implementations.Where Lock replaces synchronized methods and statements, Condition replaces Object monitor methods.
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
condition.await() //Wait for the signal and release the lock kind like wait()
condition.signal() //Send signal to for threads that are waiting for this condition kind like notify().
- ReadWriteLock: Many threads can have read lock at same time but only on thread will have write lock.
- StampedLock: Java 8 ships with a new kind of lock called StampedLock which also support read and write locks just like ReadWrietLocak. In contrast to ReadWriteLock the locking methods of a StampedLock return a stamp represented by a long value.
- Semaphores: Semaphore is used to control the number of concurrent threads that are using a resource.The Re-entrant locks usually grant exclusive access to variables or resources, a semaphore is capable of maintaining whole sets of permits.Its more like having tokens and once the token is used we need wait for the token to be released so that someone else can use it.
- Dead Lock: What is a dead lock ? When two threads are waiting on each other for a resource to be available, while each one as acquired lock on other resource.
e.g: Say we have two lists (list1 and list2).
In one thread get lock on list1 and list2 in the order I specified. In second thread get lock on list2 and list1 .
Both threads will never get locks on both objects due to different orders
- Preventing dead lock in the above case by using "ReentrantLock" by declaring lock per resource and getting all locks at once.
- Producer/Consumer example using synchronized keyword, wait() and notify() features.
- Producer/Consumer example using ArrayBlockingQueue: Thread safe and can be accessed by multiple threads. ArrayBlockingQueue infact uses RentrantLock inside it if you see the implementation." If the Queue is full any write calls to Queue will be a blocking call.
If queue is empty any reads will be blocking call.
- Interrupting a thread: We can interrupt a thread by calling Thread.currentThread().interrupt().
- If the current thread is in sleep an interruptedExcpetion will be thrown
- If current thread is running a flag will be set we can check that using "Thread.currentThread().isInteruppted()"
- If the thread is blocked on IO then the thread's interrupt status will be set, and the thread will receive a "java.nio.channels.ClosedByInterruptException"
- AtomicInteger: Thread safe. Internally, the atomic classes make heavy use of compare-and-swap (CAS), an atomic instruction directly supported by most modern CPU's. Those instructions usually are much faster than synchronizing via locks.So my advice is to prefer atomic classes over locks in case you just have to change a single mutable variable concurrently.
- ConcurrentHashMap:Similar to HashTable its Synchronized. ConcurrentHashMap uses multiple buckets to store data. This avoids read locks and greatly improves performance over a HashTable. Both are thread safe, but there are obvious performance wins with ConcurrentHashMap.
However when you read from a ConcurrentHashMap using get(), there are no locks,contrary to the HashTable for which all operations are simply synchronized. HashTable was released in old versions of Java whereas ConcurrentHashMap is a java 5+ thing.