//Displays all available Images >>docker images //Displays List of Containers Running >>docker ps (or) >>docker container ls //Start Container by loading latest Image >>docker run -d ngnix:latest //Start Container by loading latest Image >>docker run -d ngnix:latest //Stop Container >>docker stop CONTAINER_ID(or)CONTAINER_NAME //Display all Container irrespective of stopped or running >>docker rm CONTAINER_ID //Remove Container >>docker ps -a //Get ID of all containers >>docker ps -aq //Remove all stopped containers by ID >>docker rm $(docker ps -aq) //Remove all containers(stopped and running) by ID >>docker rm -f $(docker ps -aq) //Give CUSTOM_NAME to Container >>docker run --name CONTAINER_CUSTOM_NAME ngnix:latest //Stop Container by CUSTOM_NAME >>docker stop CONTAINER_CUSTOM_NAME //Display list of Containers Running by setting Format >>docker ps --format="CUSTOM_FORMAT" //Using Export Constant Variable >>export format = "CUSTOM_FORMAT" >>docker ps --format=${format} //Map Host Port to Docker Container Port >>docker run -d -p 8080:80 ngnix:latest
Monthly Archives: March 2025
CustomThreadPoolExecutor – Why, What, How?
Why we need CustomThreadPoolExecutor when we can use ExecutorService Framework to create and manage Threads?
ExecutorService executor = Executors.newFixedThreadPool(20);
is nothing but
return new ThreadPoolExecutor(20, 20, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
under the hoods.
ThreadPoolExecutor would be more effective if you have customized many or all of below parameters.
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
How it works?
- If fewer than corePoolSize threads are running, try to start a new thread with the given command as its first task. The call to addWorker atomically checks runState and workerCount, and so prevents false alarms that would add threads when it shouldn’t, by returning false.
- If a task can be successfully queued, then we still need to double-check whether we should have added a thread (because existing ones died since last checking) or that the pool shut down since entry into this method. So we recheck state and if necessary roll back the enqueuing if stopped, or start a new thread if there are none.
- If we cannot queue task, then we try to add a new thread. If it fails, we know we are shut down or saturated and so reject the task.
CustomThreadPoolExecutor.java
import java.util.concurrent.*; public class CustomThreadPoolExecutor extends ThreadPoolExecutor { public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } }
CustomThreadFactory.java
import java.util.concurrent.ThreadFactory; public class CustomThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { Thread th = new Thread(r); th.setPriority(Thread.NORM_PRIORITY); th.setDaemon(false); return th; } }
CustomRejectHandler.java
import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; public class CustomRejectHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("Task Rejected: "+ r.toString()); } }
Task.java
import java.util.concurrent.ThreadPoolExecutor; public class Task implements Runnable{ String taskName; ThreadPoolExecutor executor; Long timeInMilliSeconds; public Task(String taskName, ThreadPoolExecutor executor, Long timeInMilliSeconds) { this.taskName = taskName; this.executor = executor; this.timeInMilliSeconds = timeInMilliSeconds; } @Override public void run() { try{ Thread.sleep(this.timeInMilliSeconds); System.out.print("Tasks in Blocking Queue "+ this.executor.getQueue().stream().toList() + ", "); System.out.print(this.taskName + " completed by " + Thread.currentThread().getName() + " after running "+ timeInMilliSeconds +"ms" ); System.out.println(", Active Threads available "+ executor.getPoolSize()); }catch (Exception e){ } } @Override public String toString() { return "Task{" + "taskName='" + taskName + '\'' + '}'; } }
BatchProcessor.java
public class BatchProcessor { public static void main(String[] args) throws InterruptedException { ThreadPoolExecutor executor = new CustomThreadPoolExecutor(2,4, 10, TimeUnit.MINUTES, new ArrayBlockingQueue<>(2), new CustomThreadFactory(), new CustomRejectHandler()); System.out.println("Active Threads available for processing at start "+ executor.getPoolSize()); executor.submit( new Task("task1", executor, 2500L)); //Directly dealt by CorePool Thread executor.submit( new Task("task2", executor, 500L)); //Directly dealt by CorePool Thread Thread.sleep(2000L); System.out.println("Slept for 2000 Millisecond"); executor.submit( new Task("task3", executor, 200L)); //Directly dealt by CorePool Thread executor.submit( new Task("task4", executor, 1000L)); //Directly dealt by CorePool Thread executor.submit( new Task("task5", executor, 300L)); //Dealt by extra thread within Maximum Pool Size executor.submit( new Task("task6",executor, 300L)); //Directly dealt by CorePool Thread executor.shutdown(); } }
Output
Active Threads available for processing at start 0 Tasks in Blocking Queue [], task2 completed by Thread-1 after running 500ms, Active Threads available 2 Slept for 2000 Millisecond Tasks in Blocking Queue Task{'task4','task6'}, task3 completed by Thread-1 after running 200ms, Active Threads available 3 Tasks in Blocking Queue Task{'task6'}, task5 completed by Thread-2 after running 300ms, Active Threads available 3 Tasks in Blocking Queue [], task1 completed by Thread-0 after running 2500ms, Active Threads available 3 Tasks in Blocking Queue [], task6 completed by Thread-2 after running 300ms, Active Threads available 2 Tasks in Blocking Queue [], task4 completed by Thread-1 after running 1000ms, Active Threads available 1
Producer Consumer Implementation using BlockingQueue
What is BlockingQueue?
- BlockingQueue is a Interface which has 4 Implementations – LinkedBlockingQueue, ArrayBlockingQueue, PriorityBlockingQueue, SynchronousQueue
- Thread Safe: BlockingQueue implementations are thread-safe, with all methods being atomic.
- Blocking Operation: Has blocking behavior if the queue is full (for producers) or empty (for consumers).
- No Null Elements: Attempts to insert a null will result in a NullPointerException.
Two Types of BlockingQueue
- Bounded BlockingQueue: Fixed capacity, blocking producers when full.
- Unbounded BlockingQueue: Expands as needed (e.g., backed by a LinkedList), though subject to memory constraints.
Simple Producer Consumer Implementation using BlockingQueue?
- We have a queBuffer which take max of 10 printing task at a time
- The printing Task are added from PrintProducer whereas it is polled at PrintConsumer end
- When you start the thread for producer you should use start() method rather than run() as run executes by taking control of main thread whereas start() spawns two new thread which makes producer and consumer run at same time in two different threads.
PrintProducer.java
import java.util.concurrent.BlockingQueue; import java.util.concurrent.ThreadLocalRandom; public class PrintProducer extends Thread { private BlockingQueue queBuffer; public PrintProducer(BlockingQueue queBuffer) { this.queBuffer = queBuffer; } @Override public void run() { while(true){ try { Integer randomNo = ThreadLocalRandom.current().nextInt(100); queBuffer.put(randomNo); System.out.println("Added Task No " + String.valueOf(randomNo)); Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }
PrintConsumer.java
import java.util.concurrent.BlockingQueue; public class PrintConsumer extends Thread{ private BlockingQueue queBuffer; public PrintConsumer(BlockingQueue queBuffer) { this.queBuffer = queBuffer; } @Override public void run() { while(true){ try { System.out.println("Polled Task No " + queBuffer.take()); Thread.sleep(1500); } catch (InterruptedException e) { } } } }
ProcessPrints.java
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class ProcessPrints { static BlockingQueue queBuffer = new ArrayBlockingQueue(10); public static void main(String[] args) { PrintProducer objPrintProducer = new PrintProducer(queBuffer); PrintConsumer objPrintConsumer = new PrintConsumer(queBuffer); objPrintProducer.start(); objPrintConsumer.start(); } }
Output
Polled Task No 61 Added Task No 61 Added Task No 33 Added Task No 0 Polled Task No 33 Added Task No 29 Added Task No 93 Added Task No 20 Polled Task No 0 Added Task No 24 Added Task No 2 Added Task No 31 . . . .
The above code can be implemented as below as Thread takes Runnable as argument with run() method definition in lambda expression
ProcessPrints.java
public class ProcessPrints { static BlockingQueue queBuffer = new ArrayBlockingQueue(10); public static void main(String[] args) { //Producer Implementation new Thread(()->{ while(true){ try { Integer randomNo = ThreadLocalRandom.current().nextInt(100); queBuffer.put(randomNo); System.out.println("Added Task No " + String.valueOf(randomNo)); Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } }).start(); //Consumer Implementation new Thread(()->{ while(true){ try { System.out.println("Polled Task No " + queBuffer.poll()); Thread.sleep(1500); } catch (InterruptedException e) { } } }).start(); } }
Simple Thread Programs
Simple Program to print numbers using threads
NumberPrinter.java
public class NumberPrinter implements Runnable{ int number; public NumberPrinter(int number){ this.number = number; } public void run(){ System.out.println("Printing Number from Thread "+ this.number); } }
Main.java
public class Main { public static void main(String[] args) { for (int idx=1;idx<=5;idx++){ Thread objthread = new Thread(new NumberPrinter(idx)); objthread.start(); } } }
Output
Printing Number from Thread 5 Printing Number from Thread 1 Printing Number from Thread 4 Printing Number from Thread 3 Printing Number from Thread 2
Simple Program using Executor Service taking Runnable as Argument
ExecutorService is a framework which allows to create thread. Threads can be created from FixedThreadPool, CachedThreadPool and ScheduledThreadPool. submit() method takes runnable or callable object (Functional Interface Type) as argument. The Same code above can be rewritten as below
Main.java
public class Main { public static void main(String[] args) throws Exception { ExecutorService objExecService = Executors.newFixedThreadPool(2); //Lambda Expresssion passed as Argument as Runnable is FI objExecService.submit(() -> { System.out.println(Thread.currentThread().getName()); }); objExecService.shutdown(); } }
Output
pool-1-thread-1
Same code with Runnable instance passed as argument to submit
. . //Instance of Runnable passed as argument HelloThread1 objHT1 = new HelloThread1(); objExecService.submit(objHT1); . .
Output
Hello World from Thread Name (pool-1-thread-1) using Runnable
Same code with Runnable as Anonymous Class passed as argument
ExecutorService exec = Executors.newFixedThreadPool(2); //Instance of Runnable passed as Anonymous class exec.execute(new Runnable() { public void run() { System.out.println("Hello world"); } }); exec.shutdown();
Simple Program using Executor Service taking Callable as Argument
public class Main { public static void main(String[] args) throws Exception { ExecutorService objExecService = Executors.newFixedThreadPool(2); Future<String> objFuture = objExecService.submit(new HelloThread2()); System.out.println(objFuture.get()); objExecService.shutdown(); } }
Output
Hello World from Thread Name (pool-1-thread-1) using Callable
Using Lambda Expression as Submi
. . ExecutorService objExecService = Executors.newFixedThreadPool(2); Future<String> objFuture = objExecService.submit(() -> { Thread.sleep(3000); return Thread.currentThread().getName(); }); System.out.println(objFuture.get()); . .
The above could be rewritten in anonymous class as below
ExecutorService objExecService = Executors.newFixedThreadPool(2); Future<String> objFuture = objExecService.submit(new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(3000); return Thread.currentThread().getName(); } }); System.out.println(objFuture.get()); objExecService.shutdown();
Program for Creating Thread Pool and executing Task
ThreadPoolExample.java
public class ThreadPoolExample { public static void main(String args[]) { ExecutorService service = Executors.newFixedThreadPool(10); //create 10 worker threads in Thread Pool for (int i =0; i<100; i++){ service.submit(new Task(i)); //submit that to be done } service.shutdown(); } }
Task.java
final class Task implements Runnable { private int taskId; public Task(int id){ this.taskId = id; } @Override public void run() { System.out.println("Task ID : " + this.taskId +" performed by " + Thread.currentThread().getName()); } }
Task ID : 0 performed by pool-1-thread-1 Task ID : 3 performed by pool-1-thread-4 Task ID : 2 performed by pool-1-thread-3 Task ID : 1 performed by pool-1-thread-2 Task ID : 5 performed by pool-1-thread-6 Task ID : 4 performed by pool-1-thread-5