手写线程池以及个人思考

目录

前言

代码

详解

构造

线程协调

看下源码注解

思考

正确处理IllegalMonitorStateException错误

优化之处


前言

网上各种手撕线程池的demo很多,这个也是参照网上的demo,但是在细节上稍加优化,从中也可以掌握线程之间的通讯方法。

扫描二维码关注公众号,回复: 8598510 查看本文章

代码

package com.example.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Created on 2019/9/20.
 *
 * @author yangsen
 */
@Slf4j
public class MyThreadExcutor {

    /**
     * 核心数量
     */
    private static int CORENUMBER = 5;

    /**
     * 工作线程
     */
    private List<Worker> workers = null;

    /**
     * 阻塞任务队列
     */
    private static final BlockingQueue<Thread> queue = new LinkedBlockingQueue<>();

    public MyThreadExcutor() {
        init(CORENUMBER);
    }

    /**
     * 带参数构造函数
     *
     * @param poolSize 核心线程数
     */
    public MyThreadExcutor(int poolSize) {
        if (poolSize < 0) {
            poolSize = CORENUMBER;
        }
        init(poolSize);
    }

    /**
     * 初始化
     *
     * @param poolSize poolSize
     */
    private void init(int poolSize) {
        this.workers = new ArrayList<>(poolSize);
        CORENUMBER = poolSize;
        initWork(CORENUMBER);
    }

    /**
     * 初始化核心线程
     *
     * @param size 核心线程数
     */
    private void initWork(int size) {
        for (int i = 0; i < size; i++) {
            Worker worker = new Worker();
            Thread thread = new Thread(worker);
            thread.start();
            workers.add(worker);
        }
    }

    public void execute(Thread thread) {
        if (thread != null) {
            //唤醒队列
            synchronized (workers) {
                queue.add(thread);
                workers.notifyAll();
            }
        }
    }

    public void shutDown() {
        for (Worker worker : workers) {
            worker.shutDown();
        }
    }

    class Worker implements Runnable {

        /**
         * 线程运行状态
         */
        private volatile boolean running = true;

        @Override
        public void run() {
            synchronized (workers) {
                while (running && queue.isEmpty()) {
                    try {
                        workers.wait();
                    } catch (InterruptedException e) {
                        log.error("暂停线程队列失败:{}", e);
                        Thread.currentThread().interrupt();
                        return;
                    }
                }

                while (running && !queue.isEmpty()) {
                    Thread thread = queue.poll();
                    thread.run();
                    try {
                        workers.wait();
                    } catch (InterruptedException e) {
                        log.error("暂停线程队列失败:{}", e);
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
            }
        }

        public void shutDown() {
            running = false;
        }

    }

    public static void main(String[] args) throws InterruptedException {
        MyThreadExcutor excutor = new MyThreadExcutor();
        for (int i = 0; i < 30; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程:" + Thread.currentThread().getName() + "执行相关业务逻辑");
                }
            });
            if (i % 10 == 0) {
                System.out.println("休眠2秒......");
                Thread.sleep(2000);
            }
            excutor.execute(thread);
        }
    }

}


详解

很多demo都省略了最大线程数跟新建线程的对比,还有缓冲队列,以及线程池的拒绝策略。

构造

大同小异:

  • 一个List或者set的核心线程队列(进行获取线程进行执行)
  • 参数:核心线程数,以及核心线程的状态(shutdown时进行标识)jdk的线程池也一样会根据状态来进行判断执行相应的逻辑
  • 另一个就是储存线程的队列,跟我们jdk线程池的缓冲队列有所不同,这里是所有要执行的线程。

线程协调

当储存线程的队列为空的时候,需要进行阻塞。当有线程进来时,需要进行唤醒

自然想到Thread.currentThread().wait(),但是它实际也是调用Object的wait方法。

看下源码注解

IllegalMonitorStateException

大概意思:如果这个线程没有拥有对象的监视器将会报这个错误。

思考

为啥会有这种要求呢?没有对象的监视器,调用Object相关方法就会报错?

我们可以看到这些方法像wait,notifyAll,notify等等都是线程之间的通讯,再看下获取对象锁的作用,不就是原子性吗?举个栗子:A修改对象为等待,那么这时A任务对象处于等待状态,B改了对象唤醒状态,但是A还是认为对象是等待状态这就对A接下来的逻辑有影响了。

所以说通过获取对象的监视器来保证线程之间的协调是正常的,这只是个人的思考,如有不妥请多指教。

Thread.currentThread().wait()实际是执行Object的方法,所以我们需要写成  特定对象.wait()

接下来是处理IllegalMonitorStateException错误

正确处理IllegalMonitorStateException错误

先从栗子看起:

            synchronized (workers) {
                queue.add(thread);
                workers.notifyAll();
            }

上面这样是不会报错的。

synchronized 就是获取相应对象的锁,也可以理解为监视器

优化之处

像网上其他demo都是可以执行就ojk了,但是跟线程池还是有差别的,就是执行完任务就结束了,不会说等待新的线程加入(接客,西西)

逻辑:核心线程是开启的,线程队列是空的时候就行等待。如果说线程状态开启,而线程队列不为空,这拿到一个线程,就行执行。执行完它的任务就完了吗?不然 这里就是优化的地方。让它继续等下一个线程的加入。这里还能继续优化,就是判断queue是否为空,不为空的时候继续执行接下来的操作。为空的时候才进行等待。

发布了212 篇原创文章 · 获赞 30 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/weixin_38336658/article/details/101076702