The cost of starting a thread in the system is relatively high because it involves interaction with the operating system. The advantage of using a thread pool is to improve performance. When the system contains a large number of concurrent threads, it will cause a sharp decline in system performance and even cause JVM to crash. The maximum number of threads in the thread pool parameter can control the number of concurrent threads in the system not to exceed the number of times.
1. The Executors factory class is used to generate a thread pool . This factory class contains the following static factory methods to create the corresponding thread pool. The created thread pool is an ExecutorService object. The object's submit method or execute method to execute the corresponding Runnable or Callable tasks. The thread pool itself calls the shutdown() method to stop the thread pool when it is no longer needed. After calling the method, the thread pool will no longer allow tasks to be added, but will not die until all added tasks have been executed.
1. newCachedThreadPool() creates a thread pool with caching function and submits the thread created by the task (Runnable or Callable object) of the thread pool. If the execution is completed, it will be cached into the CachedThreadPool for use of the tasks that need to be executed later.
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class CacheThreadPool { static class Task implements Runnable { @Override public void run() { System.out.println(this + " " + Thread.currentThread().getName() + " AllStackTraces map size: " + Thread.currentThread().getAllStackTraces().size()); } } public static void main(String[] args) { ExecutorService cacheThreadPool = Executors.newCachedThreadPool(); //Add three tasks to the thread pool first for(int i = 0 ; i < 3; i++) { cacheThreadPool.execute(new Task()); } //After the three threads are executed, add three tasks to the thread pool again try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } for(int i = 0 ; i < 3; i++) { cacheThreadPool.execute(new Task()); } }}The execution results are as follows:
CacheThreadPool$Task@2d312eb9 pool-1-thread-1 AllStackTraces map size: 7CacheThreadPool$Task@59522b86 pool-1-thread-3 AllStackTraces map size: 7CacheThreadPool$Task@73dbb89f pool-1-thread-2 AllStackTraces map size: 7CacheThreadPool$Task@5795cedc pool-1-thread-3 AllStackTraces map size: 7CacheThreadPool$Task@256d5600 pool-1-thread-1 AllStackTraces map size: 7CacheThreadPool$Task@7d1c5894 pool-1-thread-2 AllStackTraces map size: 7
Thread objects in the thread pool are cached and reused when new tasks are executed. However, if there is a lot of concurrency, the cache thread pool will still create many thread objects.
2. newFixedThreadPool(int nThreads) creates a thread pool with a specified number of threads that can be reused by threads.
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class FixedThreadPool { static class Task implements Runnable { @Override public void run() { System.out.println(this + " " + Thread.currentThread().getName() + " AllStackTraces map size: " + Thread.currentThread().getAllStackTraces().size()); } } public static void main(String[] args) { ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); // First add three tasks to the thread pool for (int i = 0; i < 5; i++) { fixedThreadPool.execute(new Task()); } // After the three threads are executed, add three tasks to the thread pool again try { Thread.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 3; i++) { fixedThreadPool.execute(new Task()); } }}Execution results:
FixedThreadPool$Task@7045c12d pool-1-thread-2 AllStackTraces map size: 7FixedThreadPool$Task@50fa0bef pool-1-thread-2 AllStackTraces map size: 7FixedThreadPool$Task@ccb1870 pool-1-thread-2 AllStackTraces map size: 7FixedThreadPool$Task@7392b4e3 pool-1-thread-1 AllStackTraces map size: 7FixedThreadPool$Task@5bdeff18 pool-1-thread-2 AllStackTraces map size: 7FixedThreadPool$Task@7d5554e1 pool-1-thread-1 AllStackTraces map size: 7FixedThreadPool$Task@24468092 pool-1-thread-3 AllStackTraces map size: 7FixedThreadPool$Task@fa7b978 pool-1-thread-2 AllStackTraces map size: 7
3. newSingleThreadExecutor(), create a thread pool with only single threads, which is equivalent to calling newFixedThreadPool(1)
4. newSheduledThreadPool(int corePoolSize), creates a thread pool with a specified number of threads, which can execute threads after a specified delay. You can also repeat a thread in a certain period of time, knowing that you can call shutdown() to close the thread pool.
Examples are as follows:
import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class ScheduledThreadPool { static class Task implements Runnable { @Override public void run() { System.out.println("time " + System.currentTimeMillis() + " " + Thread.currentThread().getName() + " AllStackTraces map size: " + Thread.currentThread().getAllStackTraces().size()); } } public static void main(String[] args) { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3); scheduledExecutorService.schedule(new Task(), 3, TimeUnit.SECONDS); scheduledExecutorService.scheduleAtFixedRate(new Task(), 3, 5, TimeUnit.SECONDS); try { Thread.sleep(30 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } scheduledExecutorService.shutdown(); }}The operation results are as follows:
time 1458921795240 pool-1-thread-1 AllStackTraces map size: 6time 1458921795241 pool-1-thread-2 AllStackTraces map size: 6time 1458921800240 pool-1-thread-1 AllStackTraces map size: 7time 1458921805240 pool-1-thread-1 AllStackTraces map size: 7time 1458921810240 pool-1-thread-1 AllStackTraces map size: 7time 1458921815240 pool-1-thread-1 AllStackTraces map size: 7 time 1458921820240 pool-1-thread-1 AllStackTraces map size: 7
As can be seen from the run time, the task is executed in a 5-second cycle.
5. newSingleThreadScheduledExecutor() creates a thread pool with only one thread, and calls newScheduledThreadPool(1).
2. ForkJoinPool and ForkJoinTask
ForkJoinPool is an implementation class of ExecutorService. It supports dividing a task into multiple small tasks in parallel computing, and combining the calculation results of multiple small tasks into the total calculation results. It has two constructors
ForkJoinPool(int parallelism) creates a ForkJoinPool that contains parallelism threads.
ForkJoinPool(), creates ForkJoinPool by using the return value of Runtime.availableProcessors() method as the parallelism parameter.
ForkJoinTask represents a task that can be parallel and merged. It is an abstract class that implements the Future<T> interface. It has two abstract subclasses, representing the RecuriveAction of the task without a return value and the RecursiveTask with a return value. You can inherit these two abstract classes according to specific needs to implement your own objects, and then call ForkJoinPool's submit method to execute.
The RecuriveAction example is as follows, implementing parallel outputs of 0-300 numbers.
import java.util.concurrent.ForkJoinPool;import java.util.concurrent.RecursiveAction;import java.util.concurrent.TimeUnit;public class ActionForkJoinTask { static class PrintTask extends RecursiveAction { private static final int THRESHOLD = 50; private int start; private int end; public PrintTask(int start, int end) { this.start = start; this.end = end; } @Override protected void compute() { if (end - start < THRESHOLD) { for(int i = start; i < end; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } } else { int middle = (start + end) / 2; PrintTask left = new PrintTask(start, middle); PrintTask right = new PrintTask(middle, end); left.fork(); right.fork(); } } } public static void main(String[] args) { ForkJoinPool pool = new ForkJoinPool(); pool.submit(new PrintTask(0, 300)); try { pool.awaitTermination(2, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } pool.shutdown(); }}After splitting the small task, call the fork() method of the task and add it to the ForkJoinPool for execution in parallel.
RecursiveTask example, implements parallel calculation of 100 integers to sum. Split into every 20 numbers and sum them to get the result, and merge them into the final result at the end.
import java.util.Random;import java.util.concurrent.ExecutionException;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.Future;import java.util.concurrent.RecursiveTask;public class TaskForkJoinTask { static class CalTask extends RecursiveTask<Integer> { private static final int THRESHOLD = 20; private int arr[]; private int start; private int end; public CalTask(int[] arr, int start, int end) { this.arr = arr; this.start = start; this.end = end; } @Override protected Integer compute() { int sum = 0; if (end - start < THRESHOLD) { for (int i = start; i < end; i++) { sum += arr[i]; } System.out.println(Thread.currentThread().getName() + " sum:" + sum); return sum; } else { int middle = (start + end) / 2; CalTask left = new CalTask(arr, start, middle); CalTask right = new CalTask(arr, middle, end); left.fork(); right.fork(); return left.join() + right.join(); } } } public static void main(String[] args) { int arr[] = new int[100]; Random random = new Random(); int total = 0; for (int i = 0; i < arr.length; i++) { int tmp = random.nextInt(20); total += (arr[i] = tmp); } System.out.println("total " + total); ForkJoinPool pool = new ForkJoinPool(4); Future<Integer> future = pool.submit(new CalTask(arr, 0, arr.length)); try { System.out.println("cal result: " + future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException) e) { e.printStackTrace(); } pool.shutdown(); }}The execution results are as follows:
total 912ForkJoinPool-1-worker-2 sum:82ForkJoinPool-1-worker-2 sum:123ForkJoinPool-1-worker-2 sum:144ForkJoinPool-1-worker-3 sum:119ForkJoinPool-1-worker-2 sum:106ForkJoinPool-1-worker-2 sum:128ForkJoinPool-1-worker-2 sum:121ForkJoinPool-1-worker-3 sum:89cal result:912
After the subtask is executed, call the join() method of the task to obtain the subtask execution result, and then add it to obtain the final result.