一文彻底搞懂线程池

概述

创建线程本身开销大,反复创建并销毁,过多的占用内存。所以有大量线程创建考虑使用线程池。线程池不用反复创建线程达到线程的复用,更具配置合理利用cpu和内存减少了开销,性能会得到提高,还能统一管理任务

比如服务器收到大量请求,每个请求都分配线程去处理,对服务器性能考验就比较大,如果创建5个以上线程考虑使用线程池。

线程复用原理

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        //获取线程池的线程
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
        //如过task不等于null,或者获取的task不为空
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                    //复用执行
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

线程数量:

cpu密集型(加密,计算hash等):cpu核心数1到2倍左右
耗时IO型(读写数据库,文件网络读写)一般是cpu核心数多倍充分利用cpu
线程数公式:cpu核心数x(1+平均等待时间/平均工作时间)

线程池创建

线程池创建使用线程工具类创建

//线程池顶层接口
public interface Executor {
 
    void execute(Runnable command);

一般我们用Executor实现接口创建线程池

public interface ExecutorService extends Executor{
//重要方法
 /**
     * 初始化关闭过程,并不会直接关闭,在任务执行完毕后关闭,
     * 不在增加新任务
     */
    void shutdown();

 /**
     * 判断线程池是否已经关闭的状态,关闭返回true,否则返回false
     */
    boolean isShutdown();

 /**
     * 判断线程是否已经完全终止,任务已经执行完毕
     */
    boolean isTerminated();


 /**
     * 等待一定时间线程是否终止,执行完毕返回true,否则返回false
     *
     * @throws InterruptedException 如果期间被打断
     */
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
}

 /**
     * 停止所有正在执行的任务,停止处理等待的任务,并返回等待执行的任务列    
     * 表。
     *
     * @return 返回一个集合并返回
     */
    List<Runnable> shutdownNow();

}


/**
     * 线程池工具类,用来创建线程池,提供四个静态方法创建不同的线程池·   
     *返回 ExecutorService实现类也就是线程池对象(ThreadPoolExecutor)
     * 
     */
public class Executors {


}

//案例
   private static ExecutorService executorService = Executors.newFixedThreadPool(1)//提交任务,Task为一个具体线程类
            executorService.execute(new Task());

ExecutorService实现类(ThreadPoolExecutor)构造方法参数解析

 public ThreadPoolExecutor(int corePoolSize, //核心线程数
                             //最大线程数
                              int maximumPoolSize,
                              //线程保持存活时间,如果线程数多余核心数   
                              //量而且限制,会回收闲置的线程
                              long keepAliveTime,
                              //时间单位
                              TimeUnit unit,
                              //任务存储队列
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                //ThreadFactory是当线程池需要创建新线程底层使用线程工厂
                //创建,Handler是 线程无法接受新的任务的拒绝策略
             Executors.defaultThreadFactory(), defaultHandler);
    }

在这里插入图片描述
参数执行流程:
任务进来,核心线程处理,任务数多于核心数,将任务放进队列中,队列满了就创建新的线程但是线程总数小于最大线程数,如果任务多余最大线程数,就执行拒绝策略,不在接受任务
在这里插入图片描述

拒绝策略

  • 线程池已经关闭,提交任务会失败
  • 任务队列满了而且线程数线程数已经到了最大线程数提交任务失败
    拒绝策略分类
  • AbortPolicy()默认的策略,直接抛出异常
  • DiscardPolicy()直接默默丢弃
  • DiscardOldestPolicy()队列最老的丢弃
  • CallerRunsPolicy()谁提交线程谁去执行发,比较人性化,给线程池反冲

如果核心线程数等于最大线程数就会创建固定的线程数的线程池,默认线程工厂能解决大部分情况

Executors创建线程的四个静态方法

更具业务场景选择合适的线程池


 /**
     * 创建固定大小的线程数量量的线程池,核心数量等于最大数量
     * 都是nThreads,存活时间为0没有意义,无界队列,请求越来越多
     * 就会进入队列,容易OOM(内存溢出)
     *
     * @param 线程数量
     * @return 返回线程池
     * @throws IllegalArgumentException 参数异常
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }


 /**
     *创建一个只有一个线程的线程池,同样容易OOM
     *
     * @return 返回线程池
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

  /**
     * 核心数为0,最大线程数为Integer.MAX_VALUE,无论多少任务过来都会新建
     * 线程去处理,60秒会回收闲置的线程,同样容易OOM
     *
     * @return 返回线程池
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }



  /**
     * 该线程池可以安排命令在给定延迟后运行或定期执行。同样容易OOM
     * @param 核心数
     * @return 返回线程池
     * @throws 参数异常
     */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
  public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
 public static void main(String[] args) {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
        //延迟五秒执行
//        threadPool.schedule(new Task(), 5, TimeUnit.SECONDS);
           //延迟一秒秒执行,每隔三秒执行一次
        threadPool.scheduleAtFixedRate(new Task(), 1, 3, TimeUnit.SECONDS);
    }

正确创建线程池的方式

言外之意就是默认四种创建线程池的方式是不正确的,首先默认的灵活度不够,难以适应各种业务场景,而且容易OOM,阿里巴巴编程规范明确规定不要用默认的方式创建线程池,而是直接用ThreadPoolExecutor这个实现类

最佳实践

 ThreadPoolExecutor poolExecutor=new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)
        		poolExecutor.execute(Task);

//自定义线程池

/**
 * 描述:     演示每个任务执行前后放执行逻辑
 */
public class PauseableThreadPool extends ThreadPoolExecutor {

	//声明一个锁
    private final ReentrantLock lock = new ReentrantLock();
    //阻塞工具
    private Condition unpaused = lock.newCondition();
    //标志位
    private boolean isPaused;

    //一系列重写构造方法
    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
            TimeUnit unit,
            BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
            TimeUnit unit, BlockingQueue<Runnable> workQueue,
            ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
            TimeUnit unit, BlockingQueue<Runnable> workQueue,
            RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
            TimeUnit unit, BlockingQueue<Runnable> workQueue,
            ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory,
                handler);
    }
     //每次任务执行都会执行这个方法
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
       
        lock.lock();
        try {
            while (isPaused) {
            	//阻塞线程池
                unpaused.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private void pause() {
        lock.lock();
        try {
            isPaused = true;
        } finally {
            lock.unlock();
        }
    }

    public void resume() {
        lock.lock();
        try {
            isPaused = false;
            //唤醒线程池
            unpaused.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        PauseableThreadPool pauseableThreadPool = new PauseableThreadPool(10, 20, 10l,
                TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("我被执行");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 10000; i++) {
            pauseableThreadPool.execute(runnable);
        }
        Thread.sleep(1500);
        pauseableThreadPool.pause();
        System.out.println("线程池被暂停了");
        Thread.sleep(1500);
        pauseableThreadPool.resume();
        System.out.println("线程池被恢复了");

    }
}

线程池状态

  • RUNNING :接受任务并处理排队任务
  • SHUTDOWN :不接受新任务,但处理排队任务
  • STOP:不接受新任务,也不处理任务,突然终止
  • TIDYING:任务都终止,运行terminate终止线程池
  • TERMINATED:terminate()线程池不在启动
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章