Java concurrent programming (3) what is a thread pool

What is a thread pool

  Friends who learn programming will often hear words such as "thread pool" and "connection pool", but what does "pool" mean? I will tell you a story and everyone will understand: a long, long time ago, there was a bank, and only one customer handled business in a year. Over time, the number of people handling business increased by 5,000 every year. Twenty years later, the number of transactions handled by the bank has reached 100,000. In the beginning, when there was only one customer, the bank only needed to hire a temporary worker who was paid according to the number of business transactions, and dismissed when the business was completed. As the number of people doing business continued to increase, bank bosses found it too cumbersome to keep hiring pay-per-view employees, hiring and firing people every day. So the boss came up with a way to hire a few employees to stand by in the service hall all the time. When a customer comes, the teller will do business for the customer. After finishing one, continue to do business for the next customer. If there is no next customer Just stay on standby. In this way, this "smart" boss invented the "teller pool".

     The thread pool is to create several threads in advance. If there are tasks to be processed, the threads in the thread pool will process the tasks. After processing, the threads will not be destroyed, but will wait for the next task. Since creating and destroying threads consumes system resources, you can consider using thread pools to improve system performance when you want to create and destroy threads frequently.

 

Using thread pools in Java

Types of thread pools

  There are three commonly used thread pools in Java, namely FixedThreadPool, SingleThreadExecutor, and CachedThreadPool.

1)   FixedThreadPool

  This is a thread pool with a fixed number of threads. When the thread pool is created, the number of threads in the pool has been fixed. This type of thread pool is suitable when the number of threads that need to be run does not vary substantially. A fixed number also has the advantage that it pays for the high overhead of creating threads once, which is no longer needed when you use them later.

2)   SingleThreadExecutor

  This is a thread pool with a thread number of 1. All submitted tasks of this thread pool will be queued for execution in the order of submission. Single thread execution has an advantage: since there is no concurrent execution between tasks, tasks submitted to the thread pool do not interfere with each other. The result of program execution is more deterministic.

3)   CachedThreadPool

  As soon as you see Cache, you know that this is a thread pool related to cache. Every time a task is submitted to the thread pool, if there is no idle thread in the pool, the thread pool will create a thread for this task. If there is an idle thread thread, it will use the existing idle thread to execute the task. Some people may have a doubt: won't there be more and more threads in this way? Actually no, this thread pool also has a destruction mechanism. If a thread is not used within 60 seconds, the thread will be destroyed, which saves a lot of resources. CachedThreadPool is a more general thread pool, which can show excellent performance in most cases. When coding in the future, in case of indecision, use the cache (thread pool).

Use of thread pool

  The above mentioned for a long time did not say how to use this thing, and some friends can't wait. In fact, Java's thread pool is very simple to use. All you need is an interface and a class:

ExecutorService is an interface implemented by all thread pools, which can be used to define handles.

The Executors class is used to create different types of thread pools. The Executors.new***() method can return a thread pool object, where *** represents the name of the thread pool.

The specific code is as follows:

public class CreateThreadPool{
	public static void main(String[] args) {
		//Define a thread pool with 5 threads
		ExecutorService fixedTP = Executors.newFixedThreadPool(5);
		ExecutorService singleTE = Executors.newSingleThreadExecutor();
		ExecutorService cachedTP = Executors.newCachedThreadPool();
	}
}

  There are only slight differences in the way of definition between different thread pools, and the usage methods are exactly the same. The following takes the cache thread pool as an example to introduce the use of the thread pool:

class ExampleThread implements Runnable {
	@Override
	public void run() {
		System.out.println("Do something");
	}
}
public class UseThreadPool {
	public static void main(String[] args) {
		ExecutorService cachedTP = Executors.newCachedThreadPool();
		cachedTP.execute(new ExampleThread());
		cachedTP.shutdown();
	}
}

  ExampleThread implements the Runnable interface. This time, instead of using the Thread class to create threads, we use a thread pool. We can directly call the execute() method of the thread pool and pass in the ExampleThread object to create a thread. The execute() method can Receives an object of any class that implements the Runnable interface. When using the thread pool, don't forget to call the shutdown() method to close the thread pool. Objects that inherit from the Thread class can also create threads in the same way, because the Thread class also implements the Runnable interface.

  Last time we also talked about creating a thread by implementing the Callable interface, this thread cannot use the execute() method. The thread pool has another method: the submit() method receives an object that implements the Callable interface, and there is an overloaded method that receives a class that implements the Runnable method. That is to say, whether it is a class that implements Runnable or Callable, it can be used as its parameter. The submit() method also has a return value of type Future. Future is used to obtain the return value of the thread. Future is a generic class, and the generic type is the same as that of Callable. The demo code is as follows:

class ExampleThread implements Callable<String> {
	@Override
	public String call() {
		return "Some Value";
	}
}
public class UseThreadPool {
	public static void main(String[] args) {
		ExecutorService cachedTP = Executors.newCachedThreadPool();
		Future<String> future = cachedTP.submit(new ExampleThread());
		try {
			String value = future.get();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		cachedTP.shutdownNow();
	}
}

  future.get()方法,这个方法会阻塞当前线程,一直等到线程池里相应的线程执行结束的时候当前线程才会解除阻塞。因此,这个get()方法也是只有等到不得不用返回值的时候才会调用,否则会影响程序运行的效率。

  shutdown()方法还有一个孪生兄弟shutdownNow(),两者区别如下:

shutdown():调用这个方法之后就线程池就不会再接收其它任务了。

shutdownNow():调用这个方法会向所有的线程发一个中断信号(如果线程选择忽略,这个线程还是会继续执行),并且不再执行排队中的任务,将排队中的任务作为返回值返回(List<Runnable>)。

总结

  线程池是一个典型的“用空间换时间”的应用案例,在线程池中始终维护一定数量的线程,这样不必每次都创建新的线程,代价是线程即使空闲的时候也要占用内存资源。当需要频繁创建和销毁线程的时候,使用线程池可以显著提高系统的运行效率,在线程池的不同种类中,缓存线程池在通常情况下都是性能最好的。在提交任务到线程池的时候,通常实现Runnable接口的使用execute()方法提交,实现Callable接口的使用submit()方法提交。最后,使用线程池后别忘了shutdown()。公众号:今日说码。关注我的公众号,可查看连载文章。遇到不理解的问题,直接在公众号留言即可。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325645824&siteId=291194637