Thread pool [understand]
Status of the thread
NEW
Threads that have not yet started are in this state.
RUNNABLE
The thread executing in the Java virtual machine is in this state.
BLOCKED
A thread that is blocked and waiting for a monitor lock is in this state.
WAITING
A thread that waits indefinitely for another thread to perform a particular operation is in this state.
TIMED_WAITING
A thread waiting for another thread to perform an operation depending on the specified waiting time is in this state.
TERMINATED
The exited thread is in this state.
We frequently create and destroy threads, which consumes system resources and wastes time. Therefore, the API of Java language provides us with thread pool technology to help us solve this problem.
When you create a thread pool, you actually create a container that can store threads. If you need to perform thread tasks, take a thread out of the thread pool and return it to the thread pool after use.
Create a default thread pool [Master]
The API provides a tool class called Executors, which can be used to generate thread pools with different characteristics.
public static ExecutorService newCachedThreadPool() Create a thread pool where you can create new threads as needed. The maximum number of threads that can create int max public static ExecutorService newFixedThreadPool(int nThreads) Create a fixed length thread pool public static ExecutorService newSingleThreadExecutor() Create a thread pool for a single thread public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) Create a thread pool that can schedule commands to run after a given delay or execute periodically.
-
Take the newCachedThreadPool method as an example to demonstrate submitting tasks to the thread pool
//Create thread pool //1. You can create a new thread pool for threads as needed ExecutorService executorService = Executors.newCachedThreadPool(); //2. Create a fixed length thread pool //ExecutorService service = Executors.newFixedThreadPool(5); //3. Create a thread pool for a single thread //ExecutorService serive = Executors.newSingleThreadExecutor(); //Submit thread task executorService.submit(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"Yes"); } }); //Submit thread task executorService.submit(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"Yes"); } }); //Close thread pool executorService.shutdown();
Execute a thread task periodically
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3); //Perform a task periodically executorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"Yes"); } },3,5, TimeUnit.SECONDS);
ThreadPoolExecutor creates a thread pool
Thread pool can be understood as a hot pot shop. Six tables can be opened in the shop. If there are many guests, three more tables can be opened temporarily. When there are few guests, put away the three temporary tables. When the guests continue to increase and the frequently opened tables and temporary tables are used up, the queuing mechanism will be enabled, and other tasks will be queued in the blocking queue
Six normally open tables -- Number of core threads
Temporary 3 tables -- Number of temporary threads
Maximum number of tables in the store-- Maximum number of threads
Queuing channel -- Blocking queue
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Parameter interpretation:
corePoolSize - number of core threads.
maximumPoolSize - maximum number of threads.
keepAliveTime - the time that the temporary thread will survive.
unit - keepAliveTime time unit.
workQueue - block the queue.
threadFactory - Create a factory for threads.
handler - If the maximum number of threads is exceeded, the rejection scheme is.
Demonstration of creating thread pool
ThreadPoolExecutor poolExecutor=new ThreadPoolExecutor( 6, //Number of core threads 9, //Maximum number of threads 3, //free time TimeUnit.SECONDS, //Time unit: Second new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(), //The default factory object, which helps us produce threads new ThreadPoolExecutor.AbortPolicy() //Way of rejection ); //Submit task poolExecutor.submit(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"Yes"); } });
Reject policy
When the submitted thread task exceeds the maximum number of threads + blocking queue length, the reject policy will be triggered.
static class ThreadPoolExecutor.AbortPolicy Handler for the rejected task, which will throw RejectedExecutionException static class ThreadPoolExecutor.CallerRunsPolicy The handler for the rejected task, which directly runs the rejected task in the calling thread of the execute method; If the executor is closed, the task is discarded. (let the main thread perform redundant tasks) static class ThreadPoolExecutor.DiscardOldestPolicy The handler for the rejected task, which discards the oldest unprocessed request and then retries execute; If the executor is closed, the task is discarded. static class ThreadPoolExecutor.DiscardPolicy Handler for rejected tasks, which discards rejected tasks by default.
Thread safety details [understand]
volatile keyword
When multiple threads access shared data, they do not directly access the data of the main memory, but create a variable copy in the local memory of the thread, assign a value to the copy, and then re assign it to the main memory.
There may be different values of variable copies temporarily stored by multiple threads. Modifying this variable with volatile can solve the problem and force threads to obtain the latest value from main memory every time they use variables.
Atomicity
Atomicity means that multiple operations are indivisible atomic items. They either succeed or fail at the same time. For example, when a multithread performs an auto increment operation, it is not atomic. [local memory]
For the auto increment operation, count + + is taken as an example. In fact, the bottom layer is to complete three steps (these three steps are inseparable)
1. Copy a variable copy from main memory to thread local memory
2. Operate on the variable copy
3. Write the value of the variable copy back to the main memory
However, due to the randomness of the CPU, one thread may not complete these three steps, and the execution right is robbed by other threads, which destroys atomicity.
AtomicInteger class
Directly using int or Integer to add, modify and obtain variables can not guarantee atomicity, and thread safety problems may occur.
synchronized can be used to ensure thread safety. The way to solve problems in this way is from the perspective of pessimism, which is called pessimism lock.
To solve the problem of atomicity, Java provides a series of atomic classes. For example, AtomicInteger class is one of them. It also represents integers and provides some methods for self increment, acquisition and modification of variables. However, these methods can ensure atomicity and thread safety.
public int incrementAndGet()
The integer wrapped by AtomicInteger is self incremented first and then obtained. // Equivalent to int num=10; int c = ++num;
public int getAndincrement()
For the integer wrapped by AtomicInteger, it is obtained first and then self incremented. // Equivalent to int num=10; int c= num++;
public int getAndAdd(int delta)
For the integer wrapped by AtomicInteger, get it first and then increase it. // Equivalent to int num=10;
// int temp=num; // Get the value of num first
// num+=5; // Increase the value of num by 5
public int addAndGet(int delta)
For the integer wrapped by AtomicInteger, increase it first and then obtain it // Equivalent to int num=10;
// num+=5; / Add 5 to num
// int temp=num; // Value after assignment
public int getAndSet(int newValue)
For the integer wrapped by AtomicInteger, get it first and then set it
//Create atomic integer 10 AtomicInteger ai = new AtomicInteger(10); //Get the original value first, and then increase it by 5 int result = ai.getAndAdd(5); System.out.println("Original value:" + result);//10 System.out.println("Added value:" + ai);//15 //Get the original value first, and then set the new value result = ai.getAndSet(20); System.out.println("Original value:" + result); //15 System.out.println("New value:" + ai); //20
The principle of AtomicInteger class to ensure atomicity is shown in the following figure
Common thread safe classes [learn]
ArrayList and Vector
ArrayList: array structure, thread unsafe (high efficiency)
Vector: array structure, thread safe (inefficient)
HashMap and Hashtable
HashMap: hash table structure (array + linked list), thread unsafe (high efficiency)
Hashtable: hash table structure (array + linked list), thread safe (synchronous code block, low efficiency)
ConcurrentHashMap: hash table structure (array + linked list + red black tree), thread safe (synchronous code block + CAS algorithm, high efficiency)
StringBuilder and StringBuffer
StringBuilder: thread unsafe (high efficiency)
StringBuffer: thread safe (inefficient)
CountDownLatch class
Usage scenario: when a thread needs to be executed after other threads are executed.
//First child public class MyChildThread1 extends Thread { private CountDownLatch cdl; //Use the construction method to assign a value to cdl public MyChildThread1(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { //1. Children eat System.out.println("Xiao Gang has just finished eating dumplings"); //2. Say it after eating cdl.countDown(); } } //The second child public class MyChildThread2 extends Thread { private CountDownLatch cdl; //Use the construction method to assign a value to cdl public MyChildThread2(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { //1. Children eat System.out.println("Xiao Huang has finished his dumplings"); //2. Say it after eating cdl.countDown(); } } //Mother thread, wait for the first two child threads to execute before executing. public class MyMother extends Thread{ private CountDownLatch cdl; //Use the construction method to assign a value to cdl public MyMother(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { //1. Wait first try { cdl.await(); } catch (InterruptedException e) { e.printStackTrace(); } //2. Clean up the dishes and chopsticks System.out.println("After dinner and washing"); } } public static void main(String){ //Create a CountDownLatch object to record the number of threads executed CountDownLatch cd = new CountDownLatch(2); //Three threads share a CountDownLatch object (counter) new MyMother(cd).start(); new MyChildThread1(cd).start(); new MyChildThread2(cd).start(); }
Semaphore class
SemaPhore class is used to control the number of threads being executed. It is equivalent to an administrator role. It can issue licenses to threads. Only threads with licenses can execute. Threads without licenses must wait. SemaPhore's construction method allows you to specify how many threads are licensed.
public class MyRunnable implements Runnable{ //Create an object that controls the number of threads executed to 2 Semaphore sp = new Semaphore(2); @Override public void run(){ try { //Issue peer license sp.acquire(); //Code executed by thread System.out.println(Thread.currentThread().getName()+"Yes"); Thread.sleep(3000); //Release peer license sp.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Demo6 { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); for (int i = 0; i < 100; i++) { new Thread(mr,"thread "+i).start(); } } }