自己简单实现一个线程池

线程池
Java中的线程池是运用最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。

线程池的好处/作用:
1.不需要频繁的创建和销毁线程,提高相应速度
2.管理线程,避免无休止的创建线程导致资源枯竭。(不会每来一个任务就创建一个线程,线程不断的取任务执行)

线程池的方法有哪些
五个方法:
void execute(Job job): 执行一个Job,这个Job需要实现Runnable接口
void shutdown():关闭线程池
void addWorkers(int num):添加工作线程
void removeWorkers(int num):减少工作者线程
int getJobsSize():获得正在等待执行的任务数量

线程池的提交方式有哪几种
两种。第一种是execute不带返回值的提交,第二种是submit带返回值的提交。

线程池的属性有哪些
最大线程数 核心线程数 任务队列 超时时间 拒绝策略
核心线程数: 提交一个任务到线程池的时候,线程池会创建工作线程执行任务,即使其他核心线程空闲也会创建,直到达到核心线程数。
任务队列: 用来保存正在等待执行的任务的阻塞队列。
最大线程数: 线程池允许创建最大的线程数量。如果任务队列满了,并且已创建的线程数小于最大线程数,就会创建新的线程加速消耗。
超时时间: 如果在指定时间内没有任务那么线程就会撤销,不再占用CPU资源。
拒绝策略: 线程和队列都满了的情况下采取一种策略来应对到来的任务。

拒绝策略有哪些(可以说出来两三个)
直接抛异常/只用调用者所在线程来运行任务/丢弃队列中最近的一个任务,并执行当前任务/不处理把任务丢掉。

线程池的工作流程
1.判断核心线程是否已经满了。如果满了,下一步,没满就创建线程执行任务。
2.任务队列是不是满了。如果没满,把任务加入到队列中,如果满了,下一步。
3.线程池是不是满了。如果满了,采取拒绝策略,如果没满就创建新的线程加速消耗。
总结: 有任务过来先给到核心线程,核心线程处理不过来,就交给队列,队列满了,就开启最大线程数来加速消耗。

线程池初始阶段注意事项
即使一个任务提交时线程是空闲状态,只要是没达到核心线程数,就会创建新线程。

代码模拟
线程池接口

public interface ThreadPool<Job extends Runnable>{
    void execute(Job job);
    void shutdown();
    void addWorkers(int num);
    void removeWorkers(int num);
    int getJobSize();
}

工作线程实现
因为jobs是多线程共享的资源,所以在使用的时候需要先上锁。
有任务就取出来任务执行,没任务就会阻塞在jobs上面。

    class Worker implements Runnable {
        //是否在工作
        private volatile boolean running = true;
        @Override
        public void run() {
            while (running) {//取任务
                Job job = null;
                synchronized (jobs) {
                    while (jobs.isEmpty()) {
                        try {
                            System.out.println("没任务了" + Thread.currentThread().getName() + "进入阻塞");
                            jobs.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }
                    job = jobs.removeFirst();//取任务
                }
                if (job != null) {
                    job.run();//执行任务
                }
            }
        }
        public void shutdown() {
            this.running = false;
        }
    }

线程池实现

public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job> {
    //最大的工作线程数量
    private static final int MAX_WORKER_NUMBERS = 10;
    //默认的线程数量
    private static final int DEFAULT_WORKER_NUMBERS = 5;
    //最小的工作线程数量
    private static final int MIN_WORKER_NUMBERS = 1;
    //任务集合
    private final LinkedList<Job> jobs = new LinkedList<>();//任务从队尾插入 从队头取出来
    //工作线程
    private final List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>());
    //线程的编号
    private AtomicLong threadNum = new AtomicLong(0);
    //工作线程数
    private int workerNums = DEFAULT_WORKER_NUMBERS;

    public DefaultThreadPool() {//初始化线程池
        initializeWorkers(DEFAULT_WORKER_NUMBERS);
    }

    public DefaultThreadPool(int workerNums) {//指定线程
        this.workerNums = workerNums > MAX_WORKER_NUMBERS ? MAX_WORKER_NUMBERS : workerNums < MIN_WORKER_NUMBERS ? MIN_WORKER_NUMBERS : workerNums;
        initializeWorkers(this.workerNums);
    }

    private void initializeWorkers(int workerNums) {
        for (int i = 0; i < workerNums; i++) {
            Worker worker = new Worker();
            workers.add(worker);//加入到集合当中
            Thread thread = new Thread(worker, "worker thread" + threadNum.incrementAndGet());
            System.out.println("输出了" + threadNum);
            thread.start();//进入就绪态 开始工作
        }
    }

    @Override
    public void execute(Job job) {
        if (job != null) {
            synchronized (jobs) {
                jobs.addLast(job);
                jobs.notify();//唤醒因jobs阻塞的工作线程
            }
        }
    }

    @Override
    public void shutdown() {//将线程池关闭
        for (Worker worker: workers){
            worker.shutdown();//停止工作
        }
    }

    @Override
    public void addWorkers(int num) {//添加num个工作线程
        synchronized (jobs) {
            if (num + workerNums > MAX_WORKER_NUMBERS) num = MAX_WORKER_NUMBERS - workerNums;
            initializeWorkers(num);//添加num个线程
            workerNums += num;
        }
    }

    @Override
    public void removeWorkers(int num) {
        synchronized (jobs) {
            if (num >= workerNums) {
                throw new IllegalArgumentException("删除失败");
            }
            for (int i = 0; i < num; i++) {
                Worker worker = workers.get(i);
                if (workers.remove(worker)) {
                    worker.shutdown();//线程不再工作
                }
            }
            workerNums -= num;//减去数量
        }
    }

    @Override
    public int getJobSize() {
        return this.jobs.size();
    }
}

创建测试类

public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        ThreadPool threadPool = new DefaultThreadPool();
        for(int i=0;i<10;i++){
            Task task = new Task();
            threadPool.execute(task);
        }
        Thread.sleep(1000);
        threadPool.shutdown();
    }
    static class Task implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName() + " task is going...");
            }
        }
    }
}

在这个过程中,可能会有一个误区。run和start是不同的,start会开启一个新线程,但是run就等同于调用了普通方法,并不会开启新线程。
所以在Thread.currentThread().getName()打印的实际上就是工作线程的名称。

所有内容参考自《java并发编程的艺术》

posted @ 2024-03-12 09:48  YuKiCheng  阅读(88)  评论(0编辑  收藏  举报