Java多线程9:线程池的详细介绍——学习方腾飞Java并发编程的艺术

线程池

线程池的处理流程

1、过来一个任务,走2
2、检查核心线程池是否已满,满了走3;否则创建线程执行任务
3、检查队列是否已满,满了走4;否则创建线程执行任务
4、检查哈线程池是否已满,满了走5;否则创建线程执行任务
5、按照策略处理无法执行的任务(拒绝)

ThreadPoolExecutor

ThreadPoolExecutor执行示意图-来自Java并发编程的艺术书中插图
图中为ThreadPoolExecutor执行示意图-来自Java并发编程的艺术书中插图
执行步骤和我们所说的一致。
ThreadPoolExecutor的设计思路:在执行execute的时候,尽可能的避免获取全局锁。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute方法调用都是执行步骤2,而步骤2不需要获取全局锁。

ThreadPoolExecutor的使用

不使用线程池

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class MyThread11 {
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        final List<Integer> list = new LinkedList<>();
        final Random random = new Random();
        for (int i = 0; i < 20000; i++) {
            Thread thread = new Thread(() -> list.add(random.nextInt()));
            thread.start();
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(System.currentTimeMillis() - startTime);
        System.out.println(list.size());
    }
}

使用线程池

import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThread12 {
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        final List<Integer> list = new LinkedList<>();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(100, 100, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20000));
        final Random random = new Random();
        for (int i = 0; i < 20000; i++) {
            threadPoolExecutor.execute(() -> list.add(random.nextInt()));
        }
        threadPoolExecutor.shutdown();
        try {
            threadPoolExecutor.awaitTermination(1, TimeUnit.DAYS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(System.currentTimeMillis() - startTime);
        System.out.println(list.size());
    }
}

运行结果

// 不使用线程池
// 2639
// 20000
// 使用线程池
// 77
// 19948

结果:对比很明显,使用线程池技术,线程创建的个数少了,减少了线程上下文切换的时间,自然执行时间也变短了

线程池的作用

1、减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,自然也减少了线程切换的上下文时间
2、可以动态地调整线程池中工作线程的数据,防止因为消耗过多的内存导致服务器崩溃

ThreadPoolExecutor核心参数

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

1、corePoolSize

核心线程池的大小,线程池的基本大小。在创建了线程池之后,默认情况下,线程池中没有任何线程,而是等待有任务到来才创建线程去执行任务。当提交一个任务到线程池时,线程池创建一个线程执行任务,即使其他线程可以执行新任务也为创建线程,等到需要执行的任务书大于线程池的基本大小就不再创建。如果调用线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。

2、maximumPoolSize

线程池的最大线程数,这个参数表示了线程池中最多能创建的线程数量,当任务数量比corePoolSize大时,任务添加到workQueue,当workQueue满了,将继续创建线程以处理任务,maximumPoolSize表示的就是wordQueue也满了,线程池中最多可以创建的线程数量。如果使用了无界的任务队列,则此参数就没有效果了

3、keepAliveTime

只有当线程池中的线程数大于corePoolSize时,这个参数才会起作用。当线程数大于corePoolSize时,终止前多余的空闲线程等待新任务的最长时间。如果任务很多,并且每个任务执行的时间比较短,可以调大这个参数,提高线程的利用率

4、unit

keepAliveTime时间单位

5、workQueue

用于存储等待执行的任务的阻塞队列。有以下几种阻塞队列

  • ArrayBlockingQueue:基于数组,有界,FIFO(先进先出)
  • LinkedBlockingQueue:基于链表,FIFO
  • PriorityBlockingQueue:具有优先级的阻塞队列

6、threadFactory

执行程序创建新线程时使用的工厂

7、handler

超出线程范围和队列容量而使用的处理程序,有以下几种

  • AbortPolicy:默认的执行策略,只接抛出异常
  • CallerRunsPolicy:只用调用者所在线程来运行任务
  • DiscardOldestPolicy:丢弃队列中最近的一个任务,并执行当前任务
  • DiscardPolicy:不处理,丢弃
    当然,也可以实现RejectedExecutionHandler接口自定义拒绝策略

corePoolSize与maximumPoolSize理解

1、池中线程数小于corePoolSize,新任务都不排队而是直接添加新线程
2、池中线程数大于等于corePoolSize,workQueue未满,首选将新任务加入workQueue而不是添加新线程
3、池中线程数大于等于corePoolSize,workQueue已满,但是线程数小于maximumPoolSize,添加新的线程来处理被添加的任务
4、池中线程数大于大于corePoolSize,workQueue已满,并且线程数大于等于maximumPoolSize,新任务被拒绝,使用handler处理被拒绝的任务

猜你喜欢

转载自blog.csdn.net/qq_22798455/article/details/81363078