线程池原理和自定义实现


JDK1.5起,utils包提供了ExecutorService线程池的实现,主要目的是为了重复利用线程,提高系统效率。Thread是一个重量级的资源,创建、启动以及销毁都是比较耗费系统资源的,因此对线程的重复利用一种是非常好的程序设计习惯,加之系统中可创建的线程数量是有限的,线程数量和系统性能是一种抛物线的关系,也就是说当线程数量达到某个数值的时候,性能反倒会降低很多,因此对线程的管理,尤其是数量的控制更能直接决定程序的性能。

1 线程池原理

所谓线程池,通俗的理解就是有一个池子,里面存放着已经创建好的线程,当有任务提交给线程池执行时,池子中的某个线程会主动执行该任务。如果池子中的线程数量不够应付数量众多的任务时,则需要自动扩充新的线程到池子中,但是该数量是有限的,就好比池塘的水界线一样。当任务比较少的时候,池子中的线程能够自动回收,释放资源。为了能够异步地提交任务和缓存未被处理的任务,需要有一个任务队列。

一个完整的线程池应该具备如下要素:

  • 任务队列:用于缓存提交的任务。
  • 线程数量管理功能:一个线程池必须能够很好地管理和控制线程数量,可通过如下三个参数来实现:
    • 创建线程池时初始的线程数量init;
    • 线程池自动扩充时最大的线程数量max;
    • 在线程池空闲时需要释放线程但是也要维护一定数量的活跃数量或者活跃数量core。
    • 三者之间的关系是init<=activate<=max
  • 任务拒绝策略:如果线程数量已达到上限且任务队列已满,则需要有相应的拒绝策略来通知任务提交者
  • 线程工厂:主要用于个性化定制线程,比如将线程设置为守护线程以及设置线程名称等。
  • QueueSize:任务队列主要存放提交的Runnable,但是为了防止内存溢出,需要有limit数量对其进行控制。
  • Keepedalive时间:该时间主要决定线程各个重要参数自动维护的时间间隔。

2 简单实现一个线程池

2.1 接口定义

2.1.1 线程池接口定义

定义了一个线程池应该具备的基本操作和方法

public interface ThreadPool {

    /**
     * 提交任务到线程池
     * @param runnable
     */
    void execute(Runnable runnable);

    /**
     * 关闭线程池
     */
    void shutdown();

    /**
     * 获取线程池的初始化大小
     * @return
     */
    int getInitSize();

    /**
     * 获取线程池中最大的线程数量
     * @return
     */
    int getMaxSize();

    /**
     * 获取线程池中核心线程数量
     * @return
     */
    int getCoreSize();

    /**
     * 获取线程池中用于缓存任务队列的大小
     * @return
     */
    int getQueueSize();

    /**
     * 获取线程池中活跃的线程数量
     * @return
     */
    int getActivateCount();

    /**
     * 查看线程池是否已经被shutdown
     * @return
     */
    boolean isShutdown();
}
2.1.2 任务队列接口定义

任务队列主要用于缓存提交到线程池的中的任务,是一个BlockedQueue,并且有limit的限制(最大任务数量)

/**
 * @author wyaoyao
 * @date 2021/4/3 14:57
 * 任务队列主要用于缓存提交到线程池的中的任务,
 * 是一个BlockedQueue,并且有limit的限制(最大任务数量)
 */
public interface RunnableQueue {

    /**
     * 提交任务到队列
     * @param runnable
     */
    void offer(Runnable runnable);

    /**
     * 从队列
     * @return
     */
    Runnable take();

    /**
     * 获取队列中任务的数量
     * @return
     */
    int size();
}

2.1.3 线程工厂接口定义

ThreadFactory提供了创建线程的接口,以便于个性化地定制Thread,比如Thread应该被加到哪个Group中,优先级、线程名字以及是否为守护线程等。

@FunctionalInterface
public interface ThreadFactory {

    /**
     * 创建线程
     * @param runnable
     * @return
     */
    Thread createThread(Runnable runnable);
}
2.2.4 拒绝策略接口定义

要用于当Queue中的runnable达到了limit上限时,决定采用何种策略通知提交者,在这里提拱了三个默认实现

@FunctionalInterface
public interface DenyPolicy {

    /**
     * 拒绝策略逻辑实现
     * @param runnable
     * @param threadPool
     */
    void reject(Runnable runnable, ThreadPool threadPool);

    /**
     * 该策略会直接将任务丢弃
     */
    class DiscardDenyPolicy implements DenyPolicy {

        @Override
        public void reject(Runnable runnable, ThreadPool threadPool) {
            // do nothing
            System.out.println("任务队列已经满了,该任务将被直接丢弃");
        }
    }

    /**
     * 该策略会向任务提交者抛出异常
     */
    class AbortDenyPolicy implements DenyPolicy {

        @Override
        public void reject(Runnable runnable, ThreadPool threadPool) {
            throw new RunnableDenyException("任务队列已满, 当前任务将被抛弃");
        }
    }

    /**
     * 该拒绝策略会使任务在提交者所在的线程中执行任务
     */
    class RunnerDenyPolicy implements DenyPolicy {

        @Override
        public void reject(Runnable runnable, ThreadPool threadPool) {
            if(!threadPool.isShutdown()){
                runnable.run();
            }
        }
    }
    class RunnableDenyException extends RuntimeException {
        public RunnableDenyException(String message) {
            super(message);
        }
    }
}
2.2.5 任务定义

InternalTask是Runnable的一个实现,主要用于线程池内部,该类会使用到RunnableQueue,然后不断地从queue中取出某个runnable,并运行runnable的run方法

重写run方法,不能让线程执行结束后,被jvm回收

public class InternalTask implements Runnable {

    private final RunnableQueue runnableQueue;

    private volatile boolean running = true;

    public InternalTask(RunnableQueue runnableQueue) {
        this.runnableQueue = runnableQueue;
    }

    @Override
    public void run() {
        // 如果当前任务为running并且没有被中断,则其可以不断从队列中获取runnable的实例,执行其run方法
        while (this.running &&
                !(Thread.currentThread().isInterrupted())) {
            try {
                // 从队列中获取runnable的实例
                Runnable task = runnableQueue.take();
                // 执行其run方法
                task.run();
            } catch (InterruptedException e) {
                this.running = false;
                break;
            }
        }
    }

    /**
     * 停止当前线程,一般在线程池销毁和线程数量维护的时候会使用到。
     */
    public void stop() {
        this.running = false;
    }
}

2.2 线程池详细实现

2.2.1 实现任务队列

内部使用的是Java的LinkedList实现,内部核心属性:

  • 最大任务数
  • 拒绝策略
package study.wyy.thread.pool;

import java.util.LinkedList;

/**
 * @author wyaoyao
 * @date 2021/4/3 15:40
 */
public class LinkedRunnableQueue implements RunnableQueue {

    /**
     * 任务队列的存放的最大任务数
     */
    private final int limit;

    /**
     * 定义一个LinkedList,存放任务
     */
    private final LinkedList<Runnable> runnableList = new LinkedList<>();


    /**
     * 拒绝策略
     */
    private final DenyPolicy denyPolicy;

    public LinkedRunnableQueue(int limit,  DenyPolicy denyPolicy) {
        this.limit = limit;
        this.denyPolicy = denyPolicy;
    }

    /**
     * offer方法是一个同步方法,如果队列数量达到了上限,则会执行拒绝策略,
     * 否则会将runnable存放至队列中,同时唤醒take任务的线程
     *
     * @param runnable
     */
    @Override
    public void offer(Runnable runnable) {
        synchronized (this.runnableList) {
            if (this.runnableList.size() >= this.limit) {
                // 如果队列中的任务数量已经达到了限制的,就调用拒绝策略
                this.denyPolicy.reject(runnable);
            } else {
                // 将任务添加对队列尾部
                this.runnableList.addLast(runnable);
                // 唤醒阻塞中的线程
                this.runnableList.notifyAll();
            }
        }
    }

    /**
     * take方法也是同步方法,线程不断从队列中获取Runnable任务,
     * 当队列为空的时候工作线程会陷入阻塞,
     * 有可能在阻塞的过程中被中断,为了传递中断信号需要在catch语句块中将异常抛出以通知上游
     *
     * @return
     * @throws InterruptedException
     */
    @Override
    public Runnable take() throws InterruptedException {
        synchronized (this.runnableList) {
            while (this.runnableList.isEmpty()) {
                // 如果队列是空,就进入等待,等待提交任务
                // 任务队列中没有可执行任务,则当前线程将会挂起,进入runnableList关联的monitor waitset中等待唤醒(新的任务加入)
                try {
                    this.runnableList.wait();
                } catch (InterruptedException e) {
                    // 被中断时需要将该异常抛出
                    throw e;
                }
            }
            // 队列不是空,那就从队列头部取出一个任务返回
            return this.runnableList.removeFirst();
        }
    }

    /**
     * 返回当前任务队列中的任务数
     * @return
     */
    @Override
    public int size() {
        synchronized (runnableList) {
            return runnableList.size();
        }
    }
}

2.2.2 实现线程池
package study.wyy.thread.pool;


import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author wyaoyao
 * @date 2021/4/3 16:39
 */
public class BasicThreadPool extends Thread implements ThreadPool {

    /**
     * 线程池初始化时,内部的线程数量
     */
    private final int initSize;

    /**
     * 线程池中最大可存放的线程数量
     */
    private final int maxSize;

    /**
     * 线程池活跃线程数量
     */
    private final int activeSize;

    /**
     * 当前活跃的线程数量
     */
    private int activeCount;

    /**
     * 任务队列
     */
    private final RunnableQueue runnableQueue;

    /**
     * 创建线程需要的工厂
     */
    private final ThreadFactory threadFactory;

    /**
     * 线程池是否已经被shutdown
     */
    private volatile boolean isShutdown = false;

    /**
     * 工作线程
     */
    private final Queue<ThreadTask> threadTaskQueue = new ArrayDeque<>();

    private final long keepAliveTime;

    private final TimeUnit timeUnit;

    private static final ThreadFactory DEFAULT_Thread_Factory = new DefaultThreadFactory();


    private final static DenyPolicy DEFAULT_DENY_POLICY = new DenyPolicy.DiscardDenyPolicy();

    public BasicThreadPool(int initSize, int activeSize, int maxSize) {

        this(initSize, activeSize, maxSize, new LinkedRunnableQueue(10, DEFAULT_DENY_POLICY), DEFAULT_Thread_Factory, 10, TimeUnit.SECONDS);

    }


    public BasicThreadPool(int initSize,
                           int activeSize,
                           int maxSize,
                           RunnableQueue runnableQueue,
                           ThreadFactory threadFactory,
                           long keepAliveTime,
                           TimeUnit timeUnit) {
        this.initSize = initSize;
        this.activeSize = activeSize;
        this.maxSize = maxSize;
        this.runnableQueue = runnableQueue;
        this.threadFactory = threadFactory;
        this.keepAliveTime = keepAliveTime;
        this.timeUnit = timeUnit;
        this.activeCount = 0;
    }

    /**
     * 初始化线程池:
     * 1 先初始化initSize的数量的线程
     */
    private void init() {
        // 启动自己
        this.start();
        // 初始化initSize的数量的线程
        for (int i = 0; i < this.initSize; i++) {
            newThread();
        }
    }

    private void newThread() {

        // 创建任务线程,把任务队列丢进去,任务线程从队列中获取任务并执行
        InternalTask internalTask = new InternalTask(runnableQueue);
        Thread thread = this.threadFactory.createThread(internalTask);
        ThreadTask threadTask = new ThreadTask(thread, internalTask);
        // 将这些工作的线程放到工作队列中
        this.threadTaskQueue.offer(threadTask);
        // 活动线程数量++
        this.activeCount++;
        // 启动这些工作线程
        thread.start();

    }

    /**
     * 重写run方法,主要用于维护线程数量,比如扩容、回收等工作
     */
    @Override
    public void run() {
        while (!this.isShutdown && isInterrupted()) {
            try {
                // 每隔这个时间就维护一次线程池的几个核心属性:维护线程数量,比如扩容、回收等工作
                timeUnit.sleep(keepAliveTime);
            } catch (InterruptedException e) {
                // 在休眠期间如果被打断的打断的,那么就将isShutdown设置为true
                this.isShutdown = true;
                break;
            }

            synchronized (this) {
                if (this.isShutdown) {
                    // 如果线程池已经Shutdown,就跳出循环,不再进行维护工作
                    break;
                }
                System.out.println(this.runnableQueue.size() + " == " + activeCount);
                if (this.runnableQueue.size() > this.activeSize && this.activeCount < this.activeSize) {
                    // 如果任务队列中的任务数量大于线程池中规定的活跃线程数量,并且当前线程池中的线程数量小于规定的活跃线程数量
                    // 就扩容到规定的活跃线程数量
                    for (int i = this.activeCount; i < this.activeSize; i++) {
                        newThread();
                    }
                }

                if (this.runnableQueue.size() > this.maxSize && this.activeCount < this.maxSize) {
                    // 如果任务队列中的任务数量大于线程池中规定的最大线程数量,并且当前线程池中的线程数量小于最大线程数量
                    // 就扩容到规定的最大线程数量
                    for (int i = this.activeCount; i < this.maxSize; i++) {
                        newThread();
                    }
                }

                // 释放线程数,释放到active
                // 当任务队列中已经空了,并且线程池中的线程数量已经超过了规定的活跃数量
                // 那么就要释放线程池中的线程数量,释放到活跃数量即可
                if (this.runnableQueue.size() == 0 && this.activeCount > this.activeSize) {
                    for (int i = this.activeSize; i < activeCount; i++) {
                        removeThread();
                    }
                }
            }

        }
    }

    /**
     * 移除线程
     */
    private void removeThread() {
        ThreadTask threadTask = this.threadTaskQueue.remove();
        threadTask.internalTask.stop();
        this.activeCount--;
    }

    @Override
    public void execute(Runnable runnable) {
        if (this.isShutdown) {
            throw new IllegalStateException("The thread pool is destroy");
        }
        // 提交任务就是简单的将任务放到任务队列中
        this.runnableQueue.offer(runnable);
    }

    /**
     * 线程池的销毁同样需要同步机制的保护,主要是为了防止与线程池本身的维护线程引起数据冲突
     */
    @Override
    public void shutdown() {
        synchronized (this) {
            if (this.isShutdown) {
                // 如果已经关闭了,那就直接退出
                return;
            }
            this.isShutdown = true;
            threadTaskQueue.forEach(threadTask -> {
                threadTask.internalTask.stop();
                threadTask.thread.interrupt();
            });
            // 中断本身,自己也继承了Thread,重写了run方法用于扩容的逻辑
            // 所以也需要终止。
            this.isInterrupted();
        }

    }

    @Override
    public int getInitSize() {
        if (isShutdown) {
            throw new IllegalStateException("The thread pool is destroy");
        }
        return this.initSize;
    }

    @Override
    public int getMaxSize() {
        if (isShutdown) {
            throw new IllegalStateException("The thread pool is destroy");
        }
        return this.maxSize;
    }

    @Override
    public int getActiveSize() {
        if (isShutdown) {
            throw new IllegalStateException("The thread pool is destroy");
        }
        return this.activeSize;
    }

    @Override
    public int getQueueSize() {
        if (isShutdown) {
            throw new IllegalStateException("The thread pool is destroy");
        }
        return this.runnableQueue.size();
    }

    @Override
    public int getActivateCount() {
        synchronized (this) {
            return this.activeCount;
        }
    }

    @Override
    public boolean isShutdown() {
        return this.isShutdown;
    }

    private static class ThreadTask {
        public ThreadTask(Thread thread, InternalTask internalTask) {
            this.thread = thread;
            this.internalTask = internalTask;
        }

        Thread thread;

        InternalTask internalTask;
    }


    private static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger GROUP_COUNTER = new AtomicInteger(1);

        private static final ThreadGroup group = new ThreadGroup("MyThreadPool-" + GROUP_COUNTER.getAndDecrement());

        private static final AtomicInteger COUNTER = new AtomicInteger(0);

        @Override
        public Thread createThread(Runnable runnable) {
            /**
             * 参数一:线程组
             * 参数二:任务
             * 参数三:线程名字
             */
            return new Thread(group, runnable, "thread-pool-" + COUNTER.getAndDecrement());
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值