🖍️
Developer Note
  • Welcome
  • Git
    • Eslint & Prettier & Stylelint & Husky
  • Programming Language
    • JavaScript
      • Script Async vs Defer
      • Module
      • Const VS Let VS Var
      • Promise
      • Event Loop
      • Execution Context
      • Hoisting
      • Closure
      • Event Buddling and Capturing
      • Garbage Collection
      • This
      • Routing
      • Debounce and Throttle
      • Web Component
      • Iterator
      • Syntax
      • String
      • Array
      • Object
      • Proxy & Reflect
      • ProtoType
      • Class
      • Immutability
      • Typeof & Instanceof
      • Npm (Node package manager)
    • TypeScript
      • Utility Type
      • Type vs Interface
      • Any vs Unknown vs Never
      • Void and undefined
      • Strict Mode
      • Namespace
      • Enum
      • Module
      • Generic
    • Python
      • Local Development
      • Uv
      • Asyncio & Event loop
      • Context Manager
      • Iterator & Generator
      • Fast API
      • Pydantic & Data Class
    • Java
      • Compilation and Execution
      • Data Type
      • Enumeration
      • Data Structure
      • Try Catch
      • InputStream and OutputStream
      • Concurrent
      • Unicode Block
      • Build Tools
      • Servlet
      • Java 8
  • Coding Pattern
    • MVC vs MVVM
    • OOP vs Functional
    • Error Handling
    • MVC vs Flux
    • Imperative vs Declarative
    • Design Pattern
  • Web Communication
    • REST API
      • Web Hook
      • CORS issue
    • HTTPS
    • GraphQL
      • REST API vs GraphQL
      • Implementation (NodeJS + React)
    • Server-Sent Event
    • Web Socket
    • IP
    • Domain Name System (DNS)
  • Frontend
    • Progressive Web App (PWA)
    • Single Page & Multiple Page Application
    • Search Engine Optimiaztion (SEO)
    • Web bundling & Micro-frontend
      • Webpack
        • Using Webpack to build React Application
        • Using Webpack to build react library
      • Vite
      • Using rollup to build react library
      • Implementing micro frontend
    • Web Security
      • CSRF & Nonce
      • XSS
      • Click hijacking
    • Cypress
    • CSS
      • Core
        • Box Model
        • Inline vs Block
        • Flexbox & Grid
        • Pseudo Class
        • Position
      • Tailwind CSS
        • Shadcn
      • CSS In JS
        • Material UI
    • React
      • Core
        • Component Pattern
        • React Lazy & Suspense
        • React Portal
        • Error Boundary
        • Rendering Methods
        • Environment Variable
        • Conditional CSS
        • Memo
        • Forward Reference
        • High Order Component (HOC) & Custom Hook
        • TypeScript
      • State Management
        • Redux
        • Recoil
        • Zustand
      • Routing
        • React Router Dom
      • Data Fetching
        • Axios & Hook
        • React Query
        • Orval
      • Table
        • React Table
      • Form & Validation
        • React Hook Form
        • Zod
      • NextJS
        • Page Router
        • App Router
      • React Native
    • Angular
    • Svelte
      • Svelte Kit
  • Backend
    • Cache
      • Browser Cache
      • Web Browser Storage
      • Proxy
      • Redis
    • Rate limit
    • Monitoring
      • Logging
      • Distributed Tracing
    • Load Test
    • Encryption
    • Authentication
      • Password Protection
      • Cookie & Session
      • JSON Web Token
      • SSO
        • OAuth 2.0
        • OpenID Connect (OIDC)
        • SAML
    • Payment
      • Pre-built
      • Custom
    • File Handling
      • Upload & Download (Front-end)
      • Stream & Buffer
    • Microservice
      • API Gateway
      • Service Discovery
      • Load Balancer
      • Circuit Breaker
      • Message Broker
      • BulkHead & Zipkin
    • Elastic Search
    • Database
      • SQL
        • Group By vs Distinct
        • Index
        • N + 1 problem
        • Normalization
        • Foreign Key
        • Relationship
        • Union & Join
        • User Defined Type
      • NOSQL (MongoDB)
      • Transaction
      • Sharding
      • Lock (Concurrency Control)
    • NodeJS
      • NodeJS vs Java Spring
      • ExpressJS
      • NestJS
        • Swagger
        • Class Validator & Validation Pipe
        • Passport (Authentication)
      • Path Module
      • Database Connection
        • Integrating with MYSQL
        • Sequalize
        • Integrating with MongoDB
        • Prisma
        • MikroORM
        • Mongoose
      • Streaming
      • Worker Thread
      • Passport JS
      • JSON Web Token
      • Socket IO
      • Bull MQ
      • Pino (Logging)
      • Yeoman
    • Spring
      • Spring MVC
      • Spring REST
      • Spring Actuator
      • Aspect Oriented Programming (AOP)
      • Controller Advice
      • Filter
      • Interceptor
      • Concurrent
      • Spring Security
      • Spring Boot
      • Spring Cloud
        • Resilience 4j
      • Quartz vs Spring Batch
      • JPA and Hibernate
      • HATEOS
      • Swagger
      • Unit Test (Java Spring)
      • Unit Test (Spring boot)
  • DevOp
    • Docker
    • Kubernetes
      • Helm
    • Nginx
    • File System
    • Cloud
      • AWS
        • EC2 (Virtual Machine)
        • Network
        • IAM
          • Role-Service Binding
        • Database
        • Route 53
        • S3
        • Message Queue
        • Application Service
        • Serverless Framework
        • Data Analysis
        • Machine Learning
        • Monitoring
        • Security
      • Azure
        • Identity
        • Compute Resource
        • Networking
        • Storage
        • Monitoring
      • Google Cloud
        • IAM
          • Workload Identity Federation
        • Compute Engine
        • VPC Network
        • Storage
        • Kubernetes Engine
        • App Engine
        • Cloud function
        • Cloud Run
        • Infra as Code
        • Pub/Sub
    • Deployment Strategy
    • Jenkins
    • Examples
      • Deploy NextJS on GCP
      • Deploy Spring on Azure
      • Deploy React on Azure
  • Domain Knowledge
    • Web 3
      • Blockchain
      • Cryptocurrency
    • AI
      • Prompt
      • Chain & Agent
      • LangChain
      • Chunking
      • Search
      • Side Products
Powered by GitBook
On this page
  • Thread
  • Why need thread?
  • Cycle
  • Method
  • Interrupt
  • Racing Condition
  • Synchronization
  • DeadLock
  • LiveLock
  • Producer & Consumer
  • Thread Starvation
  • Executor
  • Executor
  • Executors
  • ExecutorService
  • ThreadPoolTaskExecutor
  • Thread Pool
  • Future
  • AtomicVariable & ConcurrentHashMap

Was this helpful?

  1. Programming Language
  2. Java

Concurrent

PreviousInputStream and OutputStreamNextUnicode Block

Last updated 7 months ago

Was this helpful?

Thread

Why need thread?

  • Sometimes, we need to run the task on background, such as: generating report, getting data from database, we don't want to do this in the main thread instead of doing it by creating a new thread

Cycle

  • We need to create a new thread , runnable class which is responsible for executing the task

	// main
	public static void main(String[] args){

		Countdown countdown = new Countdown();
		CustomRunnable customRunnable = new CustomRunnable(countdown);

		Thread thread1 = new Thread(customRunnable);
		Thread thread2 = new Thread(customRunnable);
		thread1.setName("thread1");
		thread2.setName("thread2");
		thread1.start();
		thread2.start();

	}
public class CustomRunnable implements Runnable{
    private Countdown countdown;

    public CustomRunnable(Countdown countdown) {
        this.countdown = countdown;
    }

    @Override
    public void run() {
        countdown.startCount();
    }
}

Method

Join

  • Wait for other thread to finish for several time

	Thread thread3 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("Thread 3 start");
			}
		});

	Thread thread4 = new Thread(new Runnable() {
		@Override
		public void run() {
			try {
				thread3.join();
				System.out.println("Thread 4 start");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	});

	thread4.start();
	thread3.start();
	
	// Output:
	// Thread 3 start
	// Thread 4 start

Interrupt

  • To wake the sleeping thread up

Thread thread3 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.currentThread().sleep(5000);
				} catch (InterruptedException e) {
					System.out.println("Thread 3 wake up");
				}
				System.out.println("Thread 3 start");
			}
		});

		Thread thread4 = new Thread(new Runnable() {
			@Override
			public void run() {
				thread3.interrupt();
				System.out.println("Thread 4 start");
			}
		});

		thread4.start();
		thread3.start();
		
		// Output:
		// Thread 4 start 
		// Thread 3 wake up
		// Thread 3 start

Racing Condition

  • As threads shares the same heap, if threads access the same objects at the same time, racing condition/ thread interference will be occur. We cannot ensure the order of event

public class Countdown {
    private Integer count = 0;
    public void startCount(){
        while (count < 10) {
            System.out.println("Thread" + Thread.currentThread().getName() + " Count : " + count);
            count++;
        }
    }
}
public static void main(String[] args){

		Countdown countdown = new Countdown();
		CustomRunnable customRunnable = new CustomRunnable(countdown);

		Thread thread1 = new Thread(customRunnable);
		Thread thread2 = new Thread(customRunnable);

		thread1.setName("thread1");
		thread2.setName("thread2");
		thread1.start();
		thread2.start();
	}
public class CustomRunnable implements Runnable{
    private Countdown countdown;

    public CustomRunnable(Countdown countdown) {
        this.countdown = countdown;
    }

    @Override
    public void run() {
        countdown.startCount();
    }
}
  • Countdown Object is accessed by threads at the same time, racing condition is happened

Synchronization

  • To make a scope of the code to ensure that one thread can run, other thread keep waiting until releasing the scope by thread

public class Countdown {

    private Integer count = 0;
    public void startCount(){
        synchronized (count) {
            while (count < 10) {
                System.out.println("Thread" + Thread.currentThread().getName() + " Count : " + count);
                count++;
            }
        }
    }
}

DeadLock

  • The task of 2 threads rely on the release of the lock to finish task at the same time, therefore, the task of 2 threads cannot be finished, the object lock will be kept forever

public class Deadlock {
    Object lock1 = new Object();
    Object lock2 = new Object();

    public void test1(){
        synchronized (lock1){
            System.out.println("Test1 Holding lock 1");
            synchronized (lock2){
                System.out.println("Test1 Holding lock 2");
            }
        }
    }
    public void test2(){
        synchronized (lock2){
            System.out.println("Test2 Holding lock 2");
            synchronized (lock1){
                System.out.println("Test2 Holding lock 1");
            }
        }
    }
}
public class DeadLockRunnable implements Runnable{
    private Deadlock deadlock;

    public DeadLockRunnable(Deadlock deadlock) {
        this.deadlock = deadlock;
    }

    @Override
    public void run() {
        deadlock.test1();
    }
}
		// main
		Deadlock deadlock = new Deadlock();
		DeadLockRunnable deadLockRunnable  = new DeadLockRunnable(deadlock);
		DeadLockRunnable2 deadLockRunnable2 = new DeadLockRunnable2(deadlock);
		Thread thread1 = new Thread(deadLockRunnable);
		Thread thread2 = new Thread(deadLockRunnable2);

		thread1.setName("thread1");
		thread2.setName("thread2");
		thread1.start();
		thread2.start();
		
		//output:
		// test2 Holding lock2
		// test1 Holding lock1

LiveLock

  • The task of 2 threads will keep looping and failed to release the lock

public class LivelockExample {

    private Lock lock1 = new ReentrantLock(true);
    private Lock lock2 = new ReentrantLock(true);

    public static void main(String[] args) {
        LivelockExample livelock = new LivelockExample();
        new Thread(livelock::operation1, "T1").start();
        new Thread(livelock::operation2, "T2").start();
    }

    public void operation1() {
        while (true) {
            tryLock(lock1, 50);
            print("lock1 acquired, trying to acquire lock2.");
            sleep(50);

            if (tryLock(lock2)) {
                print("lock2 acquired.");
            } else {
                print("cannot acquire lock2, releasing lock1.");
                lock1.unlock();
                continue;
            }

            print("executing first operation.");
            break;
        }
        lock2.unlock();
        lock1.unlock();
    }

    public void operation2() {
        while (true) {
            tryLock(lock2, 50);
            print("lock2 acquired, trying to acquire lock1.");
            sleep(50);

            if (tryLock(lock1)) {
                print("lock1 acquired.");
            } else {
                print("cannot acquire lock1, releasing lock2.");
                lock2.unlock();
                continue;
            }

            print("executing second operation.");
            break;
        }
        lock1.unlock();
        lock2.unlock();
    }

    // helper methods

}

Producer & Consumer

  • As we need to make a order that receiving function should wait for the sending function,

  • we can make good use of wait and notify, when notify is not received, the task will stop in while loop

  • Until notify is made, the loop will be break and keep running the process

public class Packet {
    private String packet 
    private boolean transfer = true;
    
    // Producer

    public synchronized send(String packet) {
        while (!transfer) {
            try {
                wait();
                System.out.println("send waiting....");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        this.packet = packet;
        transfer = false;
        notifyAll();
    }
    
    // Consumer

    public synchronized String receive()  {
        while (transfer) {
            try {
                wait();
                System.out.println("receive waiting....");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        transfer = true;
        notifyAll();
        return packet;
    }
}
public class Producer implements Runnable{
    private Packet packet;

    public Producer(Packet packet) {
        this.packet = packet;
    }

    @Override
    public void run() {
        List<String> stringList = new ArrayList<>();
        stringList.add("1");
        stringList.add("2");
        stringList.add("3");
        stringList.add("End");
        stringList.forEach(s -> {
           packet.send(s);
        });
    }
}
public class Consumer implements Runnable{
    private Packet packet;

    public Consumer(Packet packet) {
        this.packet = packet;
    }

    @Override
    public void run() {

        for (String str = packet.receive(); !str.equals("End"); str = packet.receive()) {
            System.out.println(str);
        }
    }
}
  • Wait and notify can only be used within synchronized scope

  • Wait should be used in while loop

Thread Starvation

  • Assume that there are many threads, by using synchronized scope, the task of random thread will be chosen to run when a thread is finished

  • However, there will be possibility that a thread will be wait for longer and longer and never be chosen to run unluckily

  • In such a case, we can make good use of fair lock to replace the synchronized scope

  • The thread having longest suspending time will be chosen to run automatically

public class Countdown {

    private Integer count = 0;
    private static ReentrantLock lock = new ReentrantLock(true);
    public void startCount() {

        while (count < 10) {
            lock.lock();
            try {
                System.out.println("Thread" + Thread.currentThread().getName() + " Count : " + count);
                count++;
            }
            finally {
                lock.unlock();
            }
        }
    }
}
  • From the result, we can see that the order is based on waiting time

  • Unlike synchronization, we must need to add back unlock so as to release to another thread

Executor

Executor

  • Executor provides a flexibility to run the task in main thread directly or create a new thread to run the task

public class CustomExecutor implements Executor {
    @Override
    public void execute(Runnable command) {
        Thread thread = new Thread(command);
        thread.start();
    }
}

Executors

  • It is a factory class which provide a template for creating executor based on the thread pool

	public static void main(String[] args){
		Countdown countdown = new Countdown();
		CustomRunnable customRunnable = new CustomRunnable(countdown);
		CustomExecutor executor1 = new CustomExecutor();
		Executor executor2 = Executors.newSingleThreadExecutor();
		executor1.execute(customRunnable);
		executor2.execute(customRunnable);
	}

ExecutorService

  • It is class extended from executor class, which provide additional method - submit which can return a future as a result

  • However, the executor service still running even the task is finished and wait for accepting new task, so we need to shutdown the thread manually

public static void main(String[] args){
		Countdown countdown = new Countdown();
		CustomRunnable customRunnable = new CustomRunnable(countdown);
		CustomExecutor executor1 = new CustomExecutor();
		Executor executor2 = Executors.newSingleThreadExecutor();
		ExecutorService executor3 = Executors.newSingleThreadExecutor();
		executor1.execute(customRunnable);
		executor2.execute(customRunnable);
		Future<Void> future = (Future<Void>) executor3.submit(customRunnable);
		executor3.shutdown();
	}

ThreadPoolTaskExecutor

  • It is a class extended from executor class, we can easily customize the thread setting on that

ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(1);
threadPoolTaskExecutor.setQueueCapacity(50);
threadPoolTaskExecutor.setMaxPoolSize(10);

Thread Pool

  • Used to control then number of task can be run at the same time

  • Core Pool Size: The number of thread created in the beginning

  • Queue Capacity: The number of thread that can be suspended

  • Max Pool Size: The max number of thread that can be run at the same time

  • When the number of thread is exceed the core pool size , the task of thread will be suspended , put in the query

  • The query is full, the new thread pool will be created so as to run the task of the thread, and the position of query will also be released

  • If the total number of thread created is larger than the queue capacity and the max pool size, the task of the thread will be rejected and throw the exception

  • The setting of core pool size can be 0 and cannot larger than the max pool size

Future

  • It is the return value of a thread

  • It can be used the stop the task even it is running and check whether the task is cancelled or finished

Future<Void> future = (Future<Void>) executor3.submit(customRunnable);
	future.cancel(true);
	if(future.isDone()|| future.isCancelled()){
		System.out.println("The thread is finished");
	}

AtomicVariable & ConcurrentHashMap

  • Use them when the variable have chance to be changed by multiple threads at the same time

  • It is thread-safe even without using synchronization scope

Output
Output
Output