自定义线程池

我这两天看完了“无暴雨”老师讲的自己写一个线程池,以理解juc包里的线程池部分。自己跟着写了一个简单的线程池。
先来整理一下思路。附上代码和运行结果作证。最后将很巧妙的地方整理。

1.线程池原理
堆里的一个线程池对象,含有已经创建好的线程对象。当外部调用线程池对象提交任务的方法,线程池会将任务放到一个阻塞队列维护。当外部提交的任务超出队列元素上限,执行拒绝策略。拒绝策略有放弃这个任务、将拒绝策略异常抛出给执行提交任务的线程、提交任务的去执行任务。当线程任务队列中有任务,线程池中的线程会执行任务。运行一段时间后,队列中仍然有任务,线程池会创建新的线程,去执行任务。继续运行一段时间后,队列中仍然有任务,线程池会创建线程个数至上限。继续运行一段时间,队列中任务为0,线程池会销毁线程。
2.实现
ThreadPool接口:线程池需要有的功能。

package com.bjsxt.chapter24;

/**
 * 线程池接口
 */
public interface ThreadPool {
    // 关闭线程池
    void shutdown();
    // 提交任务到线程池
    void execute(Runnable task);
    // 查看线程是否被关闭
    boolean isShutdown();
    // 获取初始化的线程个数
    int getInitSize();
    // 获取核心线程数
    int getCoreSize();
    // 获取最大线程数
    int getMaxSize();
    // 获取活动线程数
    int getActiveCount();
    // 获取线程池中缓存任务队列大小
    int getQueueSize();
}

RunnableQueue接口:

package com.bjsxt.chapter24;

/**
 * 任务队列:主要用来缓存提交到线程池的任务
 */
public interface RunnableQueue {
    // 入队
    void offer(Runnable runnable);
    // 出队
    Runnable take();
    // 队长度
    int size();
}

DenyPolicy接口及其内部实现类:

package com.bjsxt.chapter24;

/**
 * 拒绝策略
 */
@FunctionalInterface
public interface DenyPolicy {
    // 为什么选择 任务,线程池?这两个作为参数?
    void reject(Runnable runnable,ThreadPool threadPool);
    // 自定义拒绝策略 直接蒋拒绝策略的实现类写在接口里面,我一般不会这样写,看到这样写之后,我想到以后我也可以尝试着使用
    // 新的方式,在类比较多的情况下,蒋简单的实现类卸载他的借口中
    // 直接讲任务丢弃
    class DiscardDenyPolicy implements DenyPolicy{
        @Override
        public void reject(Runnable runnable, ThreadPool threadPool) {
            System.out.println(runnable+" 任务已经被丢弃.");
        }
    }
    // 向任务提交者抛出异常
    class AbortDenyPolicy implements DenyPolicy{
        @Override
        public void reject(Runnable runnable, ThreadPool threadPool) {
            throw new RunnableDenyException("任务 "+ runnable+ " 将被终止.");
        }
    }
    // 任务提交者所在线程中执行任务
    class RunnerDenyPolicy implements DenyPolicy{
        @Override
        public void reject(Runnable runnable, ThreadPool threadPool) {
            // 线程池没关闭才执行任务
            if(!threadPool.isShutdown())
                runnable.run();
        }
    }
}

RunnableDenyException类:

package com.bjsxt.chapter24;

/**
 * 拒绝策略中跑出的异常类
 */
public class RunnableDenyException extends RuntimeException {
    public RunnableDenyException(String s) {
        super(s);
    }
}

ThreadFactory接口及其内部实现类:

package com.bjsxt.chapter24;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 创建线程的工厂!
 * 线程的线程组,线程名称可以个性化的定义,thread-pool-0
 *
 *  @FunctionalInterface 可以用lamda 8写
 */
@FunctionalInterface
public interface ThreadFactory {
    Thread createThread(Runnable runnable);
    // !再次的内部类的写法!
    class DefaultThreadFactory implements ThreadFactory{
        final AtomicInteger COUNTER=new AtomicInteger(0);
        final AtomicInteger GROUP_COUNTER=new AtomicInteger(0);
        // 给线程组加上后缀名字
        final ThreadGroup GROUP=new ThreadGroup("myThreadGroupPool-"+GROUP_COUNTER.getAndIncrement());
        @Override
        public Thread createThread(Runnable runnable) {

            int suffix=COUNTER.getAndIncrement();
            return new Thread(GROUP,runnable,"my-Thread-Pool-thread-"+suffix);
        }
    }
}

InternalTask类:这个类真的让我惊叹!后面说原因。

package com.bjsxt.chapter24;

/**
 * 任务类
 * 取出缓存队列中的任务,执行
 */
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() {
         Runnable task=null;
         // 如果当前任务为 running 并且没有中断,泽不断的从 queue 中获取 runnbale 执行
         while(running && !Thread.currentThread().isInterrupted()){
             task=runnableQueue.take();
             task.run();
         }
    }
    // 停止当前任务,主要会在线程池的 shutdown 方法中使用
    public void stop(){
        this.running=false;
    }
}

LinkedRunnableQueue类:

package com.bjsxt.chapter24;

import java.util.LinkedList;

/**
 * RunnableQueue:缓存任务的阻塞队列接口的实现类
 *
 * 缓存外面mian线程,或者其他线程执行execute(Runnable r),r任务的.
 */
public class LinkedRunnableQueue implements RunnableQueue {
    // 链表 方便取第一个 删最后一个
    private final LinkedList<Runnable> runnableQueue=new LinkedList<>();
    // 上限
    private final int MAX_SIZE;
    // 拒绝策略 减轻用户负担 默认设置一个丢弃任务的拒绝策略
    private DenyPolicy denyPolicy;
    // 线程池引用变量 当执行拒绝策略的让提交任务的线程执行任务这个拒绝策略时,会判断线程池是否关闭,如果关闭的话,就说明用户不想执行任务了!所以会用到线程池。
    private final ThreadPool threadPool;

    public LinkedRunnableQueue(int MAX_SIZE, DenyPolicy denyPolicy, ThreadPool threadPool) {
        this.MAX_SIZE = MAX_SIZE;
        this.threadPool = threadPool;
        this.denyPolicy=denyPolicy;
    }

    @Override
    public void offer(Runnable runnable) {
        synchronized (runnableQueue){
            if (runnableQueue.size()>=MAX_SIZE){
                denyPolicy.reject(runnable,threadPool);
                return;// 过去写的是 wait,经测试发现问题。如果写的 wait 会做两遍 松鼠鳜鱼,因为 插不进来县城阻塞,当take所在线程唤醒,被执行,会从阻塞的pc中取指令,接着做菜。但是拒绝策略中已经扔掉、或者做了菜、或者抛出异常了。应该不做这个任务。
            }
            runnableQueue.addLast(runnable);
            runnableQueue.notifyAll();
        }
    }

    @Override
    public Runnable take() {
        synchronized (runnableQueue){
            if (runnableQueue.size()<=0){// 如果链表里面数量是0,肯定也是不能取出数据的
                try {
                    runnableQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            runnableQueue.notifyAll();
            return runnableQueue.removeFirst();
        }
    }

    @Override
    public int size() {
        synchronized (runnableQueue){
            return runnableQueue.size();
        }
    }
}

BasicThreadPool类:ThreadPool的实现类

package com.bjsxt.chapter24;


import java.util.ArrayDeque;
import java.util.concurrent.TimeUnit;

public class BasicThreadPool extends Thread implements ThreadPool {
    // 基本的数量 初始化线程个数、核心线程个数、最大线程个数、缓存任务队列的长度、线程池这个线程的启停变量
    private final int initSize;
    private final int coreSize;
    private final int maxSize;
    private int queueSize;
    private boolean isRunning=true;
    // 外面Main线程提交一个任务就放到任务队列里缓存。一个缓存任务的队列
    private final RunnableQueue runnableQueue;
    // 活跃线程列表
    // 工作任务类
    private ArrayDeque<ThreadTask> threadTaskList= new ArrayDeque<ThreadTask>();
    private final long keepAliveTime;
    private final TimeUnit timeUnit;
    private final static DenyPolicy DEFAULT_DENY_POLICY = new DenyPolicy.DiscardDenyPolicy();
    private class ThreadTask {
        Thread thread;
        InternalTask task;

        public ThreadTask(Thread thread, InternalTask task) {
            this.thread = thread;
            this.task = task;
        }
    }
    // 线程工厂
    private final static ThreadFactory DEFAULT_THREAD_FACTORY=new ThreadFactory.DefaultThreadFactory();
    private final ThreadFactory threadFactory;
    // 构造器
    // 给基本变量赋值
    // 创建缓存任务队列
    public BasicThreadPool(int initSize, int coreSize, int maxSize, int queueSize) {
        this(initSize,coreSize,maxSize,queueSize,DEFAULT_THREAD_FACTORY,DEFAULT_DENY_POLICY,10,TimeUnit.SECONDS);
    }
    public BasicThreadPool(int initSize, int coreSize, int maxSize, int queueSize, ThreadFactory threadFactory,
                           DenyPolicy denyPolicy,long keepAliveTime,TimeUnit timeUnit){
        this.initSize=initSize;
        this.coreSize=coreSize;
        this.maxSize=maxSize;
        this.queueSize=queueSize;
        this.threadFactory=threadFactory;
        this.keepAliveTime=keepAliveTime;
        this.timeUnit=timeUnit;
        this.runnableQueue=new LinkedRunnableQueue(queueSize,denyPolicy,this);
        init();
    }
    // 启动自维护
    // 创建线程
    private void init() {
        start();
        createThread(initSize);
    }

    private void createThread(int num) {
        int i=0;
        while (i<num){
            // 线程池内部任务,循环获取每一个缓存的任务
            InternalTask task = new InternalTask(runnableQueue);
            Thread thread = DEFAULT_THREAD_FACTORY.createThread(task);
            ThreadTask threadTask = new ThreadTask(thread,task);
            threadTaskList.addLast(threadTask);
            // 启动线程
            thread.start();
            i++;
        }
    }

    @Override
    public void run() {
        while (!isShutdown() && !this.isInterrupted()) {
            try {
                // 每5s进行一次自维护,第一次是在调用了构造方法后的5s维护
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this){// 同时操作 threadTaskList 会导致数据不一致。与shutdown方法
                if(runnableQueue.size()>0 && initSize<coreSize){
                    createThread(coreSize-initSize);
                    continue;
                }
                if(runnableQueue.size()>0 && coreSize<maxSize){
                    createThread(maxSize-coreSize);
                }
                if(runnableQueue.size()==0 && threadTaskList.size()>coreSize){
                    System.out.println(threadTaskList.size()-coreSize);
                    removeThread(threadTaskList.size()-coreSize);//
                }
            }
        }
    }

    private void removeThread(int num) {
        if(!isShutdown() && !isInterrupted()){
            while(num!=0){
                ThreadTask threadTask = threadTaskList.removeFirst();
                System.out.println(threadTask.thread.getName()+" will remove.");
                threadTask.task.stop();
                // threadTask.thread.interrupt();
                num--;
            }
        }
    }

    @Override
    public void shutdown() {
        if(isShutdown())
            return;
        synchronized (this){
            isRunning=false;
            // 关闭线程池的资源
            for(ThreadTask threadTask:threadTaskList){
                threadTask.task.stop();
                System.out.println(threadTask.thread.getName()+" will end. ");
            }
            System.out.println("thread pool thread shutdown end. ");
        }
    }

    @Override
    public void execute(Runnable task) {
        if(this.isShutdown())
            throw new IllegalStateException("thread pool already shutdown.");
        runnableQueue.offer(task);

    }

    @Override
    public boolean isShutdown() {
        return !isRunning;
    }

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

    @Override
    public int getCoreSize() {
        if(this.isShutdown()) {
            throw new IllegalStateException("thread pool already shutdown.");
        }
        return coreSize;
    }

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

    @Override
    public int getActiveCount() {
        if(this.isShutdown()) {
            throw new IllegalStateException("thread pool already shutdown.");
        }
        return threadTaskList.size();
    }

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


}

测试类:
使用XP编程的思想:测试一点,写能过测试的代码,如此重复,通过测试,完成功能。
test1:线程池运行做菜的任务。能做菜即可。

package com.bjsxt.chapter24.test;

import com.bjsxt.chapter24.BasicThreadPool;
import com.bjsxt.chapter24.ThreadPool;

import java.util.concurrent.TimeUnit;
public class Main01 {
    public static void main(String[] args) {
        // 线程池的创建
        ThreadPool threadPool = new BasicThreadPool(3,5,5,5);
        // 模拟的做菜任务
        String [] deliciousFood = {"糖醋鱼","清蒸鱼","酸菜鱼","麻辣鱼","松鼠鳜鱼"};
        // 任务的提交
        for(int i=0;i<5;i++){
            int index=i;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " will make " + deliciousFood[index] +"."+"(runnable address is "+this+")");
                    System.out.println("-------------------");
                    try {
                        // 模拟厨师做一道菜的时间 3s
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " maked " + deliciousFood[index]+"."+"(runnable address is "+this+")");
                }
            };
            threadPool.execute(runnable);
        }
}
}

运行结果:
在这里插入图片描述

test2:缓存任务满了,拒绝策略3种的测试

package com.bjsxt.chapter24.test;

import com.bjsxt.chapter24.BasicThreadPool;
import com.bjsxt.chapter24.ThreadPool;

import java.sql.Time;
import java.util.concurrent.TimeUnit;

/**
 * test 2 缓存任务满了,拒绝策略3种的测试
 */
public class Main02 {
    public static void main(String[] args) {
        ThreadPool threadPool=new BasicThreadPool(2,3,5,3);
        String [] deliciousFood = {"糖醋鱼","清蒸鱼","酸菜鱼","麻辣鱼","松鼠鳜鱼"};
        for(int i=0;i<deliciousFood.length;i++){
            int val=i;
            Runnable task=new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+ " will make "+deliciousFood[val]+"(task address is "+this+")");
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+ " maked "+deliciousFood[val]+"(task address is "+this+")");
                }
            };
            threadPool.execute(task);
        }
    }
}

运行结果
在这里插入图片描述
test3:

package com.bjsxt.chapter24.test;

import com.bjsxt.chapter24.BasicThreadPool;
import com.bjsxt.chapter24.ThreadPool;

import java.util.concurrent.TimeUnit;

/**
 * test3 线程池销毁
 * 5道菜的任务,2个厨子,每个厨子做一个菜要3秒。
 * 2秒后销毁后线程池,之后不应该做菜。有三道菜是没有做的。
 */
public class Main03 {
    public static void main(String[] args) {
        ThreadPool threadPool=new BasicThreadPool(2,3,5,3);

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            threadPool.shutdown();
        },"end-Thread").start();

        String [] deliciousFood = {"糖醋鱼","清蒸鱼","酸菜鱼","麻辣鱼","松鼠鳜鱼"};
        for(int i=0;i<deliciousFood.length;i++){
            int val=i;
            Runnable task=new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+ " will make "+deliciousFood[val]+"(task address is "+this+")");
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+ " maked "+deliciousFood[val]+"(task address is "+this+")");
                }
            };
            threadPool.execute(task);
        }
    }
}

运行结果
执行stop,依然炒完菜了,是因为InternalTask里面run里面的循环,取完任务后一定会执行他的run.
在这里插入图片描述
test4:

package com.bjsxt.chapter24.test;

import com.bjsxt.chapter24.BasicThreadPool;
import com.bjsxt.chapter24.ThreadPool;

import java.util.concurrent.TimeUnit;

/**
 * test4 线程池自维护测试
 */
public class Main04 {
    public static void main(String[] args) {
        // 构造器 创建最大缓冲数量为3个任务的缓冲队列,创建1个厨师
        ThreadPool threadPool = new BasicThreadPool(1,3,5,10);

        String [] deliciousFood = {"糖醋鱼","清蒸鱼","酸菜鱼","麻辣鱼","松鼠鳜鱼","糖醋鱼1","清蒸鱼1","酸菜鱼1","麻辣鱼1","松鼠鳜鱼1"};
        for(int i=0;i<deliciousFood.length;i++){
            int val=i;
            Runnable task=new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+" will make "+deliciousFood[val]);
                    try {
                        // 做菜时间 3秒
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+" maked "+deliciousFood[val]);
                }
            };
            // 任务提交到线程池,线程池提交到缓冲队列
            threadPool.execute(task);
        }

        while(true){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("-----------------------------");
            System.out.println("thread pool 's initSize : "+threadPool.getInitSize());
            System.out.println("thread pool 's coreSize : "+threadPool.getCoreSize());
            System.out.println("thread pool 's maxSize : "+threadPool.getMaxSize());
            System.out.println("thread pool 's activeCount : "+threadPool.getActiveCount());
            System.out.println("thread pool 's queueSize : "+threadPool.getQueueSize());
            System.out.println("-----------------------------");
        }
    }
}

运行结果:
在这里插入图片描述
两次扩容,initSize:1->coreSize:3->maxSize:5
在这里插入图片描述销毁到coreSize:3
在这里插入图片描述

3.总结
线程池是堆里的一个对象,这个对象里面维护一个队列引用变量,缓存外部提交到线程池中的任务。如果他的长度是5,只能缓存五个菜谱。如果再来一个菜谱,就会执行拒绝策略。

(1)InternalTask
过去我会为每一个任务创建一个线程对象,调用start执行任务。也就是说会放5个厨师去炒5道菜!线程对象创建会占用堆空间,线程对象调用start方法jvm会准备程序计数器,java虚拟机栈,本地方法栈的内存资源。即便是将线程对象的创建,start调用写在循环中,当一次循环结束这个线程对象就会被回收。这样vm仍然会分配线程私有的内存资源。回收机制也在执行。
但是看见这个类里面这样写:

public void run() {
     Runnable task=null;
     // 如果当前任务为 running 并且没有中断,泽不断的从 queue 中获取 runnbale 执行
     while(running && !Thread.currentThread().isInterrupted()){
         task=runnableQueue.take();
         task.run();
     }
}

仔细观察,只创建一个线程对象并启动,就可以执行这个内部类的run方法,就会获取每一个外部提交的任务的对象,并执行其中的业务逻辑。这样过去创建5个线程,启动5个线程,回收5个线程的工作量,就被降低到创建一个线程,启动一个线程,回收一个线程啦!
(2)ThreadFactory
内部类的写法,这样看起来会比较简便。以及为什么过去使用juc包下的线程池,名字都与Thread的默认名称不同,是因为在他的实现类中可以通过创建Thread,构造器穿进去个性化的名称。

final AtomicInteger COUNTER=new AtomicInteger(0);
final AtomicInteger GROUP_COUNTER=new AtomicInteger(0);
// 给线程组加上后缀名字
final ThreadGroup GROUP=new ThreadGroup("myThreadGroupPool-"+GROUP_COUNTER.getAndIncrement());
@Override
public Thread createThread(Runnable runnable) {

    int suffix=COUNTER.getAndIncrement();
    return new Thread(GROUP,runnable,"my-Thread-Pool-thread-"+suffix);
}

(3)BasicThreadPool
为什么自维护逻辑里面和shutdown逻辑里面要加synchronized?
因为他们都会取线程池启停变量,都有可能操作活动线程队列,删除元素、访问元素。如果不加锁,一边删除,一边访问,数据是不一致的。

public void shutdown() {
    if(isShutdown())
        return;
    synchronized (this){
        isRunning=false;
        // 关闭线程池的资源
        for(ThreadTask threadTask:threadTaskList){
            threadTask.task.stop();
            System.out.println(threadTask.thread.getName()+" will end. ");
        }
        System.out.println("thread pool thread shutdown end. ");
    }
}
public void run() {
    while (!isShutdown() && !this.isInterrupted()) {
        try {
            // 每5s进行一次自维护,第一次是在调用了构造方法后的5s维护
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (this){// 同时操作 threadTaskList 会导致数据不一致。与shutdown方法
            if(runnableQueue.size()>0 && initSize<coreSize){
                createThread(coreSize-initSize);
                continue;
            }
            if(runnableQueue.size()>0 && coreSize<maxSize){
                createThread(maxSize-coreSize);
            }
            if(runnableQueue.size()==0 && threadTaskList.size()>coreSize){
                System.out.println(threadTaskList.size()-coreSize);
                removeThread(threadTaskList.size()-coreSize);//
            }
        }
    }
}

使用throw可以不用返回值。

public int getInitSize() {
    if(this.isShutdown()) {
        throw new IllegalStateException("thread pool already shutdown.");
    }
    return initSize;
}

意思就是不会获得值,仅仅会抛出异常。

public class Main {
    public static void main(String[] args) {
        int i=get();
        System.out.println(i);
    }

    private static int get() {
        if(true)
            throw new IllegalStateException("no state");
        return 0;
    }
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值