Java practice and analysis of thread pool

Posted by dsnhouse on Thu, 06 Feb 2020 08:13:29 +0100

Catalog

Why do I need thread pools?

demand

Thread pool design

1. Number of threads

2,keepAliveTime

3. The task queuing policy is linkedblockingqueue

4. Reject policy

Source code analysis

Result analysis

attach

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.

 

Published 4 original articles, won praise 0, visited 3366
Private letter follow

Topics: Java socket less network