自定义线程池

思考问题?

1、线程创建的越多越好吗?

2、场景分析:如果有一亿任务,每一个任务过来我都要创建一个线程,那代表我要总共创建一亿个线程,这样会出现什么问题?

3、线程池有什么优点?

线程池原理

先思考:

1、为什么是这种结构,还有其他结构能自定义线程池?

2、你能说出来背后的逻辑吗?讲给自己听听,自己能否听得懂自己的话?

图片逻辑分析:

main 方法创造任务,threadPool里面的线程拿到任务要执行,本来只需要这两个东西 ,一个创建,一个消费就结束了,那为什么还需要中间的阻塞队列呢?

问题的关键在于我还没创建任务,你就问我要任务,我怎么给,只能你先给我排队;你创建了这么多任务,我只有三个线程来消费你的任务,多余的我也不能处理,多余的先给我排队去,我处理完手上的工作再处理你们。

手写线程池

先思考:阻塞队列如何写呢?有哪些变量,有哪些方法供别人使用呢?



import lombok.extern.slf4j.Slf4j;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j(topic = "c.BlockingQueue")
class BlockingQueue<T> {
    //1、任务队列 因为需要先进先出 所以用链表来实现
    private Deque<T> queue = new ArrayDeque<>();
    //2、需要加一把锁,因为线程池中的多个线程都需要从任务队列头部获取任务,要保证只能让一个线程获取
    private ReentrantLock lock = new ReentrantLock();
    /**
     * 队列中能存多少个任务,需要定义,那问题来了,队列中任务满了或者没任务该怎么处理?
     * 需要让线程池中的线程知道队列中没有任务,提供任务方需要让它也知道队伍已经满了
     */
    //3、生产者条件
    private Condition fullWait = lock.newCondition();
    // 4、消费者条件变量
    private Condition emptyWait = lock.newCondition();
    //5、需要一个容量
    private int capacity;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    /**
     * 条件变量已经有了 但我们需要具体的方法支持我们往队伍中添加任务和获取任务
     */
    //6、向队伍中获取任务
    public T take() {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                try {
                    emptyWait.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            T t = queue.removeFirst();
            fullWait.signal();
            return t;
        } finally {
            lock.unlock();
        }
    }

    // 7、向队伍添加任务
    public void put(T element) {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                try {
                    log.debug("等待加入任务队列{}",element);
                    fullWait.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            log.debug("加入任务队列{}",element);
            queue.addLast(element);
            emptyWait.signal();
        } finally {
            lock.unlock();
        }
    }
    // 8、获取容器大小

    public int size() {
        lock.lock();
        try {
            return queue.size();
        } finally {
            lock.unlock();
        }

    }

    //    带超时的阻塞获取方法
    public T poll(long time, TimeUnit unit) {
        lock.lock();
        try {
            long nanos = unit.toNanos(time);
            while (queue.isEmpty()) {
                try {
                    if (nanos < 0) {
                        return null;
                    }
// nanos= 返回的时总时长-已使用时间
                    nanos = emptyWait.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            T t = queue.removeFirst();
            fullWait.signal();
            return t;
        } finally {
            lock.unlock();
        }
    }
    public void tryPut(RejectPolicy<T> rejectPolicy, T task) {
        lock.lock();
        try {
            // 判断队列是否满
            if(queue.size() == capacity) {
                rejectPolicy.reject(this, task);
            } else { // 有空闲
                log.debug("加入任务队列 {}", task);
                queue.addLast(task);
                emptyWait.signal();
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 带超时时间的任务队列中添加方法
     * @param task 任务
     * @param timeout 超时时间
     * @param timeUnit 单位
     * @return
     */
    public boolean offer(T task, long timeout, TimeUnit timeUnit) {
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            while (queue.size() == capacity) {
                try {
                    if(nanos <= 0) {
                        return false;
                    }
                    log.debug("等待加入任务队列 {} ...", task);
                    nanos = fullWait.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("加入任务队列 {}", task);
            queue.addLast(task);
            emptyWait.signal();
            return true;
        } finally {
            lock.unlock();
        }
    }


}

先思考:线程池如何写呢?里面包含哪些变量呢?该有哪些方法呢?




import lombok.extern.slf4j.Slf4j;

import java.util.HashSet;
import java.util.concurrent.TimeUnit;

@Slf4j(topic = "c.ThreadPool")
public class ThreadPool {
    /**
     * 任务队列
     */
    private BlockingQueue<Runnable> taskQueue;
    /**
     * 线程队列
     */
    private HashSet<Worker> workers = new HashSet();
    /**
     * 核心线程数量
     */
    private int coreSize;

    private long timeOut;

    private TimeUnit unit;
    private RejectPolicy<Runnable> rejectPolicy;

    /**
     *
     * @param coreSize 核心线程数量
     * @param timeOut 超时时间
     * @param unit  时间单位
     * @param queueCapacity 任务队列大小
     */
    public ThreadPool(int coreSize, long timeOut, TimeUnit unit, int queueCapacity,RejectPolicy<Runnable> rejectPolicy) {
        this.coreSize = coreSize;
        this.timeOut = timeOut;
        this.unit = unit;
        this.taskQueue = new BlockingQueue<>(queueCapacity);
        this.rejectPolicy=rejectPolicy;
    }

    /**
     * 执行任务
     * @param task 任务
     */
    public void execute(Runnable task) {
        /**
         * 当任务数没有超过coreSize,直接交给worker来处理
         * 当任务数超过coreSize,放到队列中
         */
        synchronized (workers) {
            if (workers.size() < coreSize) {
                Worker worker = new Worker(task);
                log.debug("新增worker{},{}",worker,task);
                workers.add(worker);
                worker.start();
            } else {
                taskQueue.tryPut(rejectPolicy,task);
            }
        }
    }

    /**
     *  worker 来执行对应的任务
     */
    class Worker extends Thread {
        private Runnable task;

        public Worker(Runnable task) {
            this.task = task;
        }

        @Override
        public void run() {
            /**
             * 任务不为空 直接执行
             * 任务为空,判断任务队列中有无任务,有获取再执行,无从线程池中移除当前线程
             */
            while(task !=null || (task=taskQueue.poll(timeOut,unit))!=null){
                try {
                    log.debug("正在执行任务{}",task);
                    task.run();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }finally {
                    task=null;
                }
            }
            synchronized (workers) {
                log.debug("worker{}正在被移除",this);
                workers.remove(this);
            }
        }
    }
}

先思考:线程池已经写完了,但是测试哪些内容呢?任务队列满了怎么测试?对主线程有什么影响,如何改进?

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j(topic = "c.TestPool")
public class TestPool {
    public static void main(String[] args) {
        /**
         * 核心线程数2,1000毫秒过时,任务队列能装10个任务  总共12个任务
         */
        ThreadPool threadPool = new ThreadPool(2, 1000, TimeUnit.MILLISECONDS, 10);
        /**
         * 模拟现在有15个任务,看线程池怎么处理
         */
        for (int i = 0; i < 15; i++) {
           int j=i;
            threadPool.execute(()->{
                try {
                    Thread.sleep(1000000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.debug("{}",j);
            });
        }
    }
}

对主线程有什么影响?(解决方案->拒绝策略)

线程测试(这里模拟了一直等待,抛出异常,不处理,让主线程执行等策略,用到了策略模式)

@Slf4j(topic = "c.TestPool")
public class TestPool {
    public static void main(String[] args) {
        /**
         * 核心线程数1,1000毫秒过时,任务队列能装1个任务  总共2个任务
         */
        ThreadPool threadPool = new ThreadPool(1, 1000,
                TimeUnit.MILLISECONDS, 1,(queue,task)->{
//            1、死等
//            queue.put(task);
//            2、带超时时间的添加
//            queue.offer(task,1500,TimeUnit.MILLISECONDS);
//            3、放弃执行task任务
//            log.debug("放弃任务{}",task);
//            4、抛异常
//            throw new RuntimeException("任务执行失败"+task);
//            5、让主线程自己执行任务
            task.run();
        });
        /**
         * 模拟现在有15个任务,看线程池怎么处理
         */
        for (int i = 0; i < 4; i++) {
           int j=i;
            threadPool.execute(()->{
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.debug("{}",j);
            });
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值