Here is refresher of multithreading in java.
Java (since 1.5) has come up with more controlled and cleaner way to handle threads, i.e. ExecutorService.
ExecutorService is a way in which we can create thread pools and execute threads in controlled manner, i.e. can define number of threads to be allowed at the same time.
ExecutorService ex = Executors.newSingleThreadExecutor();
ExecutorService ex = Executors.newFixedThreadPool(5);
ExecutorService ex = Executors.newScheduledThreadPool(10);
In first case we are creating a singlethreaded pool, whereas in second case we are defining a pool of fixed length. In third case we are creating a pool of threads which can perform activities at a defined time.
Read more
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html
http://tutorials.jenkov.com/java-util-concurrent/executorservice.html
A simple code example
Play around with thread pool size to understand the usage.
package com.kamalmeet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadTestExecutor {
public static void main(String s[]) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executor.execute(new RunTest());
executor.execute(new RunTest2());
}
executor.shutdown();
}
}
class RunTest implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("run" + Thread.currentThread().getName());
}
}
class RunTest2 implements Runnable {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("run2" + Thread.currentThread().getName());
}
}
At this point let us dig a bit into what happens behind the scenes when we initialize an ExecutorService Instance. In Executor class we have this implementation for the newFixedThreadPool method
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
Similar to the newFixedThreadPool, we have implementation for SingleThreadExector and CachedThreadPool
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
Let’s take a look at ThreadPoolExecutor as well
/** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters, the * {@linkplain Executors#defaultThreadFactory default thread factory} * and the {@linkplain ThreadPoolExecutor.AbortPolicy * default rejected execution handler}. * * <p>It may be more convenient to use one of the {@link Executors} * factory methods instead of this general purpose constructor. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the {@code keepAliveTime} argument * @param workQueue the queue to use for holding tasks before they are * executed. This queue will hold only the {@code Runnable} * tasks submitted by the {@code execute} method. * @throws IllegalArgumentException if one of the following holds:<br> * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
If you need additional control on the type of threads being created, you can send ThreadFactory as an additional parameter to ThreadPoolExecutor.
Before closing on the topic, it is worth looking at Atomic Variables. Atomic variables provide a way to maintain a thread-safe state for variables. This implements CAS (Compare and Swap) approach, which is faster than implementing synchronization as that needs locking and unlocking mechanism. The most commonly used atomic variable classes in Java are AtomicInteger, AtomicLong, AtomicBoolean, and AtomicReference.