线程池要点

289 阅读3分钟

这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战

前言

最近学习了一下线程池,了解了一下线程池的相关内容,在这里简单的做一下记录以便之后复习,并在此推荐一下B站狂神的JUC视频【狂神说Java】JUC并发编程最新版通俗易懂,我看完后感觉讲解的非常好

池化技术

xx池,这个词汇我们经常听到,我第一听到这个词汇是在我学习JDBC时,为了提高效率,我们可以创建一个连接池来事先放入几个链接,需要的时候就拿来使用,不需要的时候在放回池中去。这样的话避免了每一次使用JDBC时都要获取和销毁连接,提高了程序的速度和效率,并且在最后的时候进行统一销毁。

从中我们可以体会到池化技术的本质上就是对资源的统一管理、事先创建、统一销毁,优化了对资源的使用。

池化技术的好处:

  1. 对资源统一管理,方便调用
  2. 提高程序在使用中的速度
  3. 对资源实现复用,减少了资源的浪费

线程池

线程池的作用:线程复用、控制最大并发数、管理线程

三大方法

使用Executors.newSingleThreadExecutor() 创建一个只有一个线程的线程池

package com.cheng.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@SuppressWarnings({"all"})
public class Test1 {
    public static void main(String[] args) {
        // 单个线程
        ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 单个线程
        /**
         * public static ExecutorService newSingleThreadExecutor() {
         *         return new FinalizableDelegatedExecutorService
         *             (new ThreadPoolExecutor(1, 1,
         *                                     0L, TimeUnit.MILLISECONDS,
         *                                     new LinkedBlockingQueue<Runnable>()));
         *     }
         */
        try{
            for (int i = 1; i <= 5; i++) {
                // 使用线程池,来创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

使用Executors.newFixedThreadPool(5);方法传入参数创建一个固定线程大小的线程池

package com.cheng.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@SuppressWarnings({"all"})
public class Test2 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);// 创建一个固定的线程池的大小
        /**
         * public static ExecutorService newFixedThreadPool(int nThreads) {
         *         return new ThreadPoolExecutor(nThreads, nThreads,
         *                                       0L, TimeUnit.MILLISECONDS,
         *                                       new LinkedBlockingQueue<Runnable>());
         *     }
         */
        try{
            for (int i = 1; i <= 5; i++) {
                // 使用线程池,来创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
    
}

使用Executors.newCachedThreadPool();方法创建一个具有弹性的线程池其最大线程数Integer.MAX_VALUE约12亿

package com.cheng.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test3 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();// 可伸缩的线程池(有弹性)
        /**
         * public static ExecutorService newCachedThreadPool() {
         *         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
         *                                       60L, TimeUnit.SECONDS,
         *                                       new SynchronousQueue<Runnable>());
         *     }
         */
        try{
            for (int i = 1; i <= 5; i++) {
                // 使用线程池,来创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

​ 从上面创建线程池的三种方式可以看出本质上都是调用了ThreadPoolExecutor方法 区别只是传入的参数有所不同。

​ 而在阿里的开发手册中强制规定了不允许使用Executors来创建线程池,而是要使用ThreadPoolExecutor方法创建线程池。

​ 使用Executors方法创建线程池有以下缺点

  1. SingleThreadPool、FixedThreadPool允许请求队列最大为Integer.MAX_VALUE,可能会导致大量请求堆积,导致OOM。
  2. CachedThreadPool 允许最大线程为Integer.MAX_VALUE,可能会创建大量线程,导致OOM。

OOM(Out Of Memory):JVM没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收(内存溢出)

七大参数

首先可以来看看ThreadPoolExecutor方法的源码

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException(); // 抛出IllegalArgumentException异常(非法数据异常)
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException(); // 抛出空指针异常
    this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

其中我们可以看到需要传入七个参数

int corePoolSize:核心线程池大小

int maximumPoolSize: 最大线程池大小

long keepAliveTime:超时时间(在时间内没有调用线程就会释放线程)

TimeUnit unit:超时单位

BlockingQueue workQueue : 阻塞队列

ThreadFactory threadFactory,:线程工厂

RejectedExecutionHandler handler : 拒绝策略

四种拒绝策略

​ new ThreadPoolExecutor.AbortPolicy() // 大于最大承载不处理抛出异常 ​ new ThreadPoolExecutor.CallerRunsPolicy() // 返回原来的线程 ​ new ThreadPoolExecutor.DiscardPolicy() // 队列满了,丢弃任务,不会抛出异常 ​ new ThreadPoolExecutor.DiscardOldestPolicy() // 尝试和最早线程的竞争