Catalog
3. The task queuing policy is linkedblockingqueue
Why do I need thread pools?
In daily development, we are prone to the following problems:
1. Excessive resource consumption
Thread pool can reuse the created threads to reduce the consumption caused by thread creation and destruction.
2. Response speed is too slow
When the task arrives, the thread pool task can be executed immediately without waiting for the thread to be created.
3. Improve thread manageability
Threads are scarce resources. If they are created without restrictions, they will not only consume system resources, but also reduce the stability of the system. Using thread pool, they can be uniformly allocated, tuned and monitored
4. Memory overflow, CPU exhaustion
Reasonable configuration of memory and use of CPU, thread pool can prevent server overload.
demand
In daily development, we often encounter multiple clients accessing the server at the same time.
When the number of clients is increasing, we should not only ensure the communication with clients, but also ensure the normal operation of the server under the supportable cost.
Thread pool design
1. Number of threads
CPU intensive:
That is to say, computational tasks, such as search and sorting, occupy more CPU resources, and should be configured with as few threads as possible, with lower efficiency. It is recommended to configure N +1 for the number of threads, and N is the number of CPU cores.
IO intensive:
That is to say, the tasks of network request and read / write memory, such as WEB application, occupy less CPU resources (because most of the time, the CPU is waiting for the completion of IO operation), and should be configured with as many threads as possible. 2 × n is recommended for the number of threads. N refers to the number of CPU cores.
This is IO intensive, so the number of threads is set to 2 × N.
2,keepAliveTime
When the client exits, the thread exits immediately, and the thread pool gets the tasks in the queue at the first time, so keepAliveTime is set to 0L.
3. The task queuing policy is linkedblockingqueue
Blocking queue |
Characteristic |
ArrayBlockingQueue |
A bounded blocking queue based on array structure, sorting tasks by FIFO |
LinkedBlockingQuene |
Based on the chained list structure, the task is sorted by FIFO, and the throughput is usually higher than arrayblockingqueue |
SynchronousQuene |
Direct submission. For a blocking queue that does not store elements, each insert operation must wait until another thread calls the remove operation. Otherwise, the insert operation is always blocked. In this task submission mode, there is no task buffer |
priorityBlockingQuene |
Unbounded blocking queue with priority |
Our task has no priority, it's only a first come first served. Due to the large number of visits, we need to accept more tasks as much as possible and store them in the buffer if necessary. Therefore, the task queuing policy selects linkedblockingqueue.
4. Reject policy
Refusal strategy |
Characteristic |
AbortPolicy |
By default, if a handler is rejected, a runtime exception is thrown. |
CallerRunsPolicy
|
The thread uses the thread of the caller to execute the task, and provides a simple feedback control mechanism, which will slow down the submission speed of new tasks. |
DiscardOldestPolicy
|
Discard the top task in the blocking queue and execute the current task, i.e. if the executing program has not been closed, the task at the head of the work queue will be deleted, and then retry the executing program (repeat the process if it fails again) |
DiscardPolicy |
Tasks that cannot be executed will be discarded without exception. |
Tasks in the queue are executed first. For tasks that cannot be executed, we need to run out of exceptions. Therefore, the default AbortPolicy is selected for the reject policy.
At this point, the parameter design of thread pool is completed.
Common thread pool |
Characteristic |
Adaptive scene |
newSingleThreadExecutor |
Single threaded thread pool |
For scenarios where sequential execution is required and only one thread is executing |
newFixedThreadPool |
Fixed size thread pool |
It is used to limit the number of threads when the concurrent pressure is known. |
newCachedThreadPool |
Unlimited thread pool |
It is suitable for processing tasks with less execution time. |
newScheduledThreadPool |
Line adaptation for delay start and timing start |
For scenarios where multiple background threads are required to perform periodic tasks. |
newWorkStealingPool |
Thread pool with multiple task queues |
You can reduce the number of connections and create threads with the current number of CPUs available for parallel execution. |
In the high concurrency state, the extra tasks are stored in the queue for waiting. When there are idle threads, the tasks in the queue are taken out immediately for execution, which ensures that the execution of the tasks will not overflow the memory at the same time.
In conclusion, the thread pool type is newFixedThreadPool.
Source code analysis
Server.java
package hptestthreadpool; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Logger; /** * @author hp */ public class Server { private Logger logger = Logger.getLogger("Server.class"); private static final int THREADPOOL_COEFFICIENT = 2; private ServerSocketChannel serverSocketChannel = null; private ExecutorService executorService; private int PORT = 23; public static void main(String args[]) throws IOException { new Server().service(); } public Server() throws IOException { executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * THREADPOOL_COEFFICIENT); //executorService = Executors.newFixedThreadPool(1); serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().setReuseAddress(true); serverSocketChannel.socket().bind(new InetSocketAddress(PORT)); logger.info("Port:" + PORT); } public void service() { while (true) { SocketChannel socketChannel = null; try { socketChannel = serverSocketChannel.accept(); executorService.execute(new Process(socketChannel)); } catch (IOException e) { e.printStackTrace(); } } } }
1. First, set the number of threads coefficient to 2, that is, the number of threads is 2xN.
2. Use the Executors tool class to create a thread pool of type newFixedThreadPool, which is responsible for communication with the client.
Executors is a tool class that provides methods for creating common thread pools. It is convenient to create ThreadPoolExecutor objects. The bottom layer calls the construction method of ThreadPoolExecutor:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
3. Open the socket and listen for the connection of the client. When a client is connected, a new thread will be opened to handle the communication task with the client.
Thread pool executes tasks mainly through the execute method of ThreadPoolExecutor class, which turns our tasks (i.e. Process class) into commands of Runnable type. The thread pool first judges whether the task can be added to the core thread. If it can no longer be added to the blocking work queue, if the queue is full, a new thread will be created in the non core thread to Process the task and develop to maxPoolSize.
Process.java
concrete tasks
package hptestthreadpool; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.nio.channels.SocketChannel; import java.util.Date; import java.util.logging.Logger; /** * @author hp */ public class Process implements Runnable { private Logger logger = Logger.getLogger("Process.class"); private SocketChannel socketChannel; public Process(SocketChannel socketChannel) { this.socketChannel = socketChannel; } @Override public void run() { try { Socket socket = socketChannel.socket(); logger.info("Client connected: " + socket.getInetAddress() + ":" + socket.getPort()); InputStream socketIn = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader( socketIn)); OutputStream socketOut = socket.getOutputStream(); PrintWriter printWriter = new PrintWriter(socketOut, true); String msg = null; while ((msg = br.readLine()) != null) { logger.info("Received:" + socket.getInetAddress() + ":" + socket.getPort() + " Content:" + msg); printWriter.println(new Date()); if (msg.equals("guanbi")) { logger.info(socket.getInetAddress() + ":" + socket.getPort() + " Closed"); break; } } } catch (IOException e) { e.printStackTrace(); } finally { try { if (socketChannel != null) { socketChannel.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
Client1.java
package hptestthreadpool; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.nio.channels.SocketChannel; import java.util.logging.Logger; /** * @author hp */ public class Client1 { private Logger logger = Logger.getLogger("Client1.class"); private SocketChannel socketChannel; private String HOST = "localhost"; private int PORT = 23; public static void main(String[] args) throws IOException { new Client1().talk(); } public Client1() throws IOException { socketChannel = SocketChannel.open(); InetSocketAddress inetSocketAddress = new InetSocketAddress(HOST, PORT); socketChannel.connect(inetSocketAddress); } public void talk() throws IOException { try { InputStream inputStream = socketChannel.socket().getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); OutputStream socketOutputStream = socketChannel.socket().getOutputStream(); PrintWriter printWriter = new PrintWriter(socketOutputStream, true); BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in)); String msg = null; while ((msg = localReader.readLine()) != null) { printWriter.println(msg); logger.info(bufferedReader.readLine()); if (msg.equals("guanbi")) { break; } } } catch (IOException e) { e.printStackTrace(); } finally { try { socketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } }
Client2.java the same
The client sends information to the server. After receiving the message, the server performs the task and outputs the message to be received. The client uses "close" as the instruction to disconnect from the server. This is not the focus of this article, just a simple logical display, interested friends can be specific.
Result analysis
First, open the server, then client 1 and send the message "11", then client 2 and send the message "22".
Client 1:
11 February 05, 2020 9:35:35 am hptestthreadpool.Client1 talk Information: Wed Feb 05 09:35:35 CST 2020 guanbi February 05, 2020 9:35:50 am hptestthreadpool.Client1 talk Message: Wed Feb 05 09:35:50 CST 2020
Client 2:
22 February 05, 2020 9:35:37 am hptestthreadpool.Client2 talk Information: Wed Feb 05 09:35:37 CST 2020 guanbi February 05, 2020 9:35:53 am hptestthreadpool.Client2 talk Information: Wed Feb 05 09:35:53 CST 2020
Server:
February 05, 2020 9:35:16 am hptestthreadpool. Server < init > Information: Port: 23 February 05, 2020 9:35:24 am hptestthreadpool.Process run Info: client connected / 127.0.0.1:49965 February 05, 2020 9:35:30 am hptestthreadpool.Process run Info: client connected / 127.0.0.1:49972 February 05, 2020 9:35:35 am hptestthreadpool.Process run Message: received / 127.0.0.1:49965 content: 11 February 05, 2020 9:35:37 am hptestthreadpool.Process run Message: received / 127.0.0.1:49972 content: 22 February 05, 2020 9:35:50 am hptestthreadpool.Process run Message: received / 127.0.0.1:49965 content: guanbi February 05, 2020 9:35:50 am hptestthreadpool.Process run Info / 127.0.0.1:49965 closed February 05, 2020 9:35:53 am hptestthreadpool.Process run Message: received / 127.0.0.1:49972 content: guanbi February 05, 2020 9:35:53 am hptestthreadpool.Process run Info / 127.0.0.1:49972 closed
If the number of threads is set to 1, only one client can communicate with the server. The running results are as follows:
Open server:
February 05, 2020 9:52:44 am hptestthreadpool. Server < init > Information: Port: 23
Run client1 and send message 11, run client2 and send message 22:
February 05, 2020 9:53:18 am hptestthreadpool.Process run Info: client connected / 127.0.0.1:50108 February 05, 2020 9:53:25 am hptestthreadpool.Process run Message: received / 127.0.0.1:50108 content: 11
At this time, the server does not execute the task of client 2, because the number of threads is set to 1, only the task of client 1 is executed.
Close client1:
February 05, 2020 9:54:00 am hptestthreadpool.Process run Message: received / 127.0.0.1:50108 content: guanbi February 05, 2020 9:54:00 am hptestthreadpool.Process run Info / 127.0.0.1:50108 closed February 05, 2020 9:54:00 am hptestthreadpool.Process run Info: client connected / 127.0.0.1:50112 February 05, 2020 9:54:00 am hptestthreadpool.Process run Message: received / 127.0.0.1:50112 content: 22
When the client 1 is disconnected from the server, the thread is released. At this time, the number of occupied threads is 0. With idle threads, the tasks of the client 2 waiting in the queue are taken out for execution.
Close client2:
February 05, 2020 9:54:07 am hptestthreadpool.Process run Message: received / 127.0.0.1:50112 content: guanbi February 05, 2020 9:54:07 am hptestthreadpool.Process run Info / 127.0.0.1:50112 closed
attach
1. You can call ThreadPoolExecutor to create thread pool by yourself
2. The RejectedExecutionHandler interface can be implemented according to the application scenario, and the rejection policy can be customized, such as logging or tasks that cannot be processed by persistent storage.