teardown cost is cheaper. • Number of threads is limited, don’t do this: class UnreliableWebServer { public static void main(String[] args) { ServerSocket socket = new ServerSocket(80); while (true) { final Socket connection = socket.accept(); Runnable r = new Runnable() { public void run() { handleRequest(connection); } }; new Thread(r).start(); } } } • Scenarios: web server, database server, communication server ,etc.
& short duration & predictable small size. • Ideal solution: work queue + thread pool = startup delay eliminated & immediate serviced. That’s what JCF uses!!! • The Executor’s implementation define different execution policies: • When and in what thread a task will run • Level of resource consumption (number of threads, etc) • Action when executor is overloaded. • Considerations: • Thread pool deadlock: i.e. newFixedThreadPool() • Tuning thread pool: ThreadFactory, RejectedExecutionHandler, Amdahl's law, etc.
newCachedThreadPool() Unbounded work queue, unlimited threads, expire not reused threads after 60 secs. newFixedThreadPool(int n) Unbounded work queue, maximum n threads, if a thread fails is replaced by a new thread. newSingleThreadExecutor() Unbounded work queue, single thread, execution sequentially, one task at the time. newScheduledThreadPool(int n) Unbounded work queue, maximum n threads, new thread per schedule.
# threads to keep in the pool, even if they are idle. int maximumPoolSize max # of threads to allow in the pool long keepAliveTime when # current threads > corePoolSize, maximum time that excess idle threads will wait for new tasks before terminating BlockingQueue<Runnable> workQueue queue to use for holding tasks before they are executed ThreadFactory threadFactory thread factory to use when the executor creates a new thread RejectedExecutionHandler handler the handler to use when execution is blocked because the thread bounds and queue capacities are reached
be executed: Runnable r = new Runnable() { @Override public void run() { // do something useful } } executor.execute(r); • Callable<T> is an interface that represent a task that will return something when completed: Callable<Object> c = new Callable<Object>() { @Override public Object call() throws Exception { // solve world hunger return new HungerGames(); } } executor.submit(c); Runnable vs Callable<T>
throws Exception { // make world peace possible return new Ingsoc(); } } Future<Object> future = executor.submit(c); // Meanwhile do something else try { Object result = future.get(); profitFromWorldPeace(result); } catch(InterruptedException | ExecutionException e) { // pretend to do something } Futures • Executors and ThreadPools allow to retrieve the return value of a task upon completion. • Reference to this future value is called Future<T>:
that couldn’t be evenly split in advance. • Suitable for spawning cascading dependent tasks. • Best suited for recursive algorithms. • Note: DO NOT confuse with POSIX’s fork() function
1 Subtask 2 Subtask 3 Subtask 4 Subtask 2 Subtask 3 Partial Result 1 Result 1 Result 2 Result 3 Result 4 Complete Result Subtask 1 Fork-and-join process Subtask 4
2 Subtask 3 Subtask 4 Subtask 2 Subtask 3 artial Result 1 Result 3 Result 4 Complete Result Fork-and-join process Subtask 4 Idle thread Partial Result 4