StackStalk
  • Home
  • Java
    • Java Collection
    • Spring Boot Collection
  • Python
    • Python Collection
  • C++
    • C++ Collection
    • Progamming Problems
    • Algorithms
    • Data Structures
    • Design Patterns
  • General
    • Tips and Tricks

Wednesday, April 23, 2014

Thread Pools in Java

 April 23, 2014     Java     No comments   

In this article, we will explore the concept of thread pools in Java with examples. To scale applications it becomes necessary to separate thread creation and management from rest of the application. Using thread pools is an approach typically employed in large scale systems. This article provides a quick overview on executor framework in Java and provides examples on how to work with thread pools.

Significance of Thread Pools

Thread Pools use worker threads to minimize thread creation overhead. Thread Pools reduce the memory management overhead which is important for large scale applications. Thread Pools allow the applications to degrade gracefully.

Executor Service Objects

Java supports executor framework which provide the enablers for thread creation and management. Listed are some of the Java objects of executor service and their key purpose.

Executor

Executor interface provides a way to decouple tasks from how a task will run, thread to use etc. Executor is normally used instead of creating threads.

ExecutorService

ExecutorService interface represents an asynchronous execution mechanism which is capable of executing tasks in the background.

ThreadPoolExecutor

ExecutorService is an interface and ThreadPoolExecutor is one of the concrete implementations of ExecutorService. Executes submitted tasks using one of the several pooled threads. ThreadPoolExecutor provides many adjustable parameters and hooks. For ease of programming it is recommended to use the static factory methods provided by Executors.

Executors

Provides factory and utility methods for executor. They provide static methods like newFixedThreadPool(), newCachedThreadPool() which are much easier to use.

Approaches for Thread Pools

Java supports several approaches for handling thread pools. These include:

Fixed thread pool

This approach reuses fixed number of threads. At any point at-most "n" threads would be active. If additional tasks are submitted when all threads are active the tasks would be queued. Threads in the pool will exist until it is explicitly shutdown.

Cached thread pool

This approach creates new threads as needed, but will reuse previously constructed threads when they are available. If no threads are available for a task a new thread will be created and added to the pool. Threads that are not used for 60 secs will be terminated and removed from the cache.

Single thread pool

This approach uses a single worker thread. Tasks submitted would be executed sequentially. This can be assumed equivalent to a fixed thread pool of size "1". The primary difference is that fixed thread pool can reconfigured to use additional threads but single thread pool is not re-configurable.

Example of Thread Pool usage

This example shows usage of fixed, cached and single thread pool.
package com.sourcetricks.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPool {
 
 class MyThread extends Thread {

  private int id;
  
  public MyThread(int id) {
   this.id = id;
  }
  
  public void run() {
   System.out.println("Starting thread " + id);
   doSomeWork();
   System.out.println("Completed thread " + id);
  }
  
  private void doSomeWork() {
   try {
    Thread.sleep(5000);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
 }
 
 // Not using thread pool
 private void doWithoutThreadPool() {
  for ( int i = 0; i < 20; i++ ) {
   MyThread thread = new MyThread(i);
   thread.start();
  }
 }
 
 // Using fixed thread pool
 private void doWithFixedThreadPool1() throws InterruptedException {
  ExecutorService executor = Executors.newFixedThreadPool(5);
  for ( int i = 0; i < 20; i++ ) {
   MyThread thread = new MyThread(i);
   executor.execute(thread);
  }
  System.out.println("Active thread count = " + ((ThreadPoolExecutor)executor).getActiveCount());
  
  // Don't accept new work
  executor.shutdown();
  
  // Wait for 30 secs for the threads to complete
  executor.awaitTermination(30, TimeUnit.SECONDS);
 }
 
 // Using fixed thread pool. Directly uses the ThreadPoolEexcutor
 // which provides finer control
 private void doWithFixedThreadPool2() throws InterruptedException {
  int core = 5;
  int max = 10;
  int keepalive = 5000;
  
  ExecutorService executor = new ThreadPoolExecutor(
    core, 
    max, 
    keepalive, 
    TimeUnit.MILLISECONDS, 
    new LinkedBlockingQueue<Runnable>());
  
  for ( int i = 0; i < 20; i++ ) {
   MyThread thread = new MyThread(i);
   executor.execute(thread);
  }
  
  System.out.println("Active thread count = " + ((ThreadPoolExecutor)executor).getActiveCount());
  executor.shutdown();
  executor.awaitTermination(30, TimeUnit.SECONDS);
 }
 
 // Using cached thread pool
 private void doWithCachedThreadPool() throws InterruptedException {
  ExecutorService executor = Executors.newCachedThreadPool();  
  for ( int i = 0; i < 20; i++ ) {
   MyThread thread = new MyThread(i);
   executor.execute(thread);
  }
  System.out.println("Active thread count = " + ((ThreadPoolExecutor)executor).getActiveCount());
 }
 
 // Using single thread pool
 private void doWithSingleThreadPool() throws InterruptedException {
  ExecutorService executor = Executors.newSingleThreadExecutor();
  // Executor returned is not reconfigurable
  for ( int i = 0; i < 20; i++ ) {
   MyThread thread = new MyThread(i);
   executor.execute(thread);
  }
 }

 public static void main(String[] args) {
  
  ThreadPool threadPool = new ThreadPool();
  try {
   threadPool.doWithCachedThreadPool();
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
}
  • Share This:  
Newer Post Older Post Home

0 comments:

Post a Comment

Follow @StackStalk
Get new posts by email:
Powered by follow.it

Popular Posts

  • Avro Producer and Consumer with Python using Confluent Kafka
    In this article, we will understand Avro a popular data serialization format in streaming data applications and develop a simple Avro Produc...
  • Monitor Spring Boot App with Micrometer and Prometheus
    Modern distributed applications typically have multiple microservices working together. Ability to monitor and manage aspects like health, m...
  • Server-Sent Events with Spring WebFlux
    In this article we will review the concepts of server-sent events and work on an example using WebFlux. Before getting into this article it ...
  • Implement caching in a Spring Boot microservice using Redis
    In this article we will explore how to use Redis as a data cache for a Spring Boot microservice using PostgreSQL as the database. Idea is to...
  • Python FastAPI microservice with Okta and OPA
    Authentication (AuthN) and Authorization (AuthZ) is a common challenge when developing microservices. In this article, we will explore how t...
  • Spring Boot with Okta and OPA
    Authentication (AuthN) and Authorization (AuthZ) is a common challenge when developing microservices. In this article, we will explore how t...
  • Getting started with Kafka in Python
    This article will provide an overview of Kafka and how to get started with Kafka in Python with a simple example. What is Kafka? ...
  • Getting started in GraphQL with Spring Boot
    In this article we will explore basic concepts on GraphQL and look at how to develop a microservice in Spring Boot with GraphQL support. ...

Copyright © StackStalk