Java自定义一个线程池

线程池图解 

线程池与主线程之间通过一个阻塞队列来平衡任务分配,阻塞队列中既可以满足线程等待,又要接收主线程的任务。

线程池实现

创建任务队列

这里我们使用一个双向链表实现阻塞任务队列,既然是阻塞队列。我们需要两个条件变量分别来控制线程来取任务时队列为空阻塞与线程来存放任务时队列为满阻塞。

public class BlockingQueue<T> {
    //双线链表
    private Deque<T> queue = new ArrayDeque();
    //锁
    private ReentrantLock lock =new ReentrantLock();
    //生产者条件变量
    private Condition fullWaitSet = lock.newCondition();
    //消费者条件变量
    private Condition emptyWaitSet = lock.newCondition();
    //容器容量大小
    private int capacity;

    //阻塞获取,指定阻塞时间,超时未存放成功则放弃
    public T pull(long timeOut, TimeUnit unit){
        lock.lock();
        //判断链表中是否存在任务待处理
        try {
            //将尝试时间转化为纳秒
            long nanos = unit.toNanos(timeOut);
            while (queue.isEmpty()){
                try {
                    if (nanos<0){
                        return null;
                    }
                    //awaitNanos返回结果是最大等待时间减去睡眠时间的剩余时间
                    nanos = emptyWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //能够执行到这里说明任务队列中存在任务
            T t = queue.removeFirst();
            //唤醒因为任务队列存放满而阻塞的生产者线程
            fullWaitSet.signal();
            return t;
        } finally {
            lock.unlock();
        }
    }

    //阻塞添加
    public void put(T element){
        lock.lock();
        try{
            while(queue.size()==capacity){
                //说明满了,暂时无法添加新的任务
                try {
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            queue.addLast(element);
            emptyWaitSet.signal();
        }finally {
            lock.unlock();
        }
    }

    //获取队列任务数量
    public int size(){
        lock.lock();
        try {
            return queue.size();
        }finally {
            lock.unlock();
        }
    }
}

创建线程池 

在线程池中,需要维护刚刚创建的阻塞队列,方便从队列中获取与存放任务,其次需要创建一个Set集合存放当前线程池中已经启动的线程,并且需要一个核心线程数来控制set集合中的线程数量不得大于核心线程数,如果核心线程数已满,那么出现新的任务需要存放在队列当中。

public class ThreadPool {
    //任务队列
    private BlockingQueue<Runnable> blockingQueue;
    //线程集合
    private HashSet<Worker> workers = new HashSet();
    //核心线程数
    private int coreNum;
    //超时时间
    private long timeOut;
    private TimeUnit unit;

    public ThreadPool(int coreNum, long timeOut, TimeUnit unit, int queueCapacity) {
        System.out.println("初始化线程池");
        this.coreNum = coreNum;
        this.timeOut = timeOut;
        this.unit = unit;
        this.blockingQueue = new BlockingQueue<>(queueCapacity);
    }

    //线程执行任务
    public void execute(Runnable task) {
        //当线程数没有超过coreNum时,直接交给Worker对象执行,如果超过了coreNum数,则加入BlockingQueue
        synchronized (workers) {
            if (workers.size() < coreNum) {
                Worker worker = new Worker(task);
                System.out.println("新增worker"+worker);
                //将工作线程加入集合中
                workers.add(worker);
                worker.start();
            } else {
                System.out.println("从消息队列中获取task");
                blockingQueue.put(task);
            }
        }
    }

    class Worker extends Thread {
        private Runnable task;

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

        @Override
        public void run() {
            //task要么从构造方法中获取,要么从阻塞队列中获取
            while (task != null || (task = blockingQueue.pull(timeOut, unit)) != null) {
                try {
                    System.out.println("Worker执行任务");
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    task = null;
                }
            }
            //当退出了while循环,说明队列中已经不存在任务了
            synchronized (workers){
                System.out.println("Worker执行完毕"+this);
                workers.remove(this);
            }
        }
    }
}

测试 

public class Test {
    public static void main(String[] args) {
        ThreadPool threadPool = new ThreadPool(3,3000, TimeUnit.MILLISECONDS,5);
        for (int i = 0; i < 10; i++) {
            int j = i;
            threadPool.execute(()->{
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产任务:"+j);
            });
        }
    }
}

执行结果如下 

初始化线程池
新增workerThread[Thread-0,5,main]
新增workerThread[Thread-1,5,main]
新增workerThread[Thread-2,5,main]
Worker执行任务
Worker执行任务
加入任务队列TheadPool.Test$$Lambda$1/1078694789@7ba4f24f
Worker执行任务
加入任务队列TheadPool.Test$$Lambda$1/1078694789@3b9a45b3
加入任务队列TheadPool.Test$$Lambda$1/1078694789@7699a589
加入任务队列TheadPool.Test$$Lambda$1/1078694789@58372a00
加入任务队列TheadPool.Test$$Lambda$1/1078694789@4dd8dc3
等待加入任务队列TheadPool.Test$$Lambda$1/1078694789@6d03e736
生产任务:2
生产任务:1
生产任务:0
Worker执行任务
Worker执行任务
加入任务队列TheadPool.Test$$Lambda$1/1078694789@6d03e736
Worker执行任务
加入任务队列TheadPool.Test$$Lambda$1/1078694789@378bf509
生产任务:3
生产任务:4
生产任务:5
Worker执行任务
Worker执行任务
Worker执行任务
生产任务:6
生产任务:8
生产任务:7
Worker执行任务
Worker执行完毕Thread[Thread-1,5,main]
Worker执行完毕Thread[Thread-0,5,main]
生产任务:9
Worker执行完毕Thread[Thread-2,5,main] 

添加拒绝策略

在上面测试中,有一点不友好的是,当任务队列满了之后,再向其中添加任务时,主线程会死等任务添加成功。

对此我们可以选择多种解决方案

  • 死等
  • 添加超时时间
  • 让调用者方式执行
  • 让调用者抛出异常
  • 让调用者自己执行

创建拒绝策略接口

@FunctionalInterface
public interface RejectPolicy<T> {
    void reject(BlockingQueue<T> queue,T task);
}

修改线程池代码

	//添加属性
	private RejectPolicy rejectPolicy;
	//构造方法
	public ThreadPool(int coreNum, long timeOut, TimeUnit unit, int queueCapacity, RejectPolicy rejectPolicy) {
        System.out.println("初始化线程池");
        this.coreNum = coreNum;
        this.timeOut = timeOut;
        this.unit = unit;
        this.blockingQueue = new BlockingQueue<>(queueCapacity);
        this.rejectPolicy = rejectPolicy;
    }

	//线程执行任务
    public void execute(Runnable task) {
        //当线程数没有超过coreNum时,直接交给Worker对象执行,如果超过了coreNum数,则加入BlockingQueue
        synchronized (workers) {
            if (workers.size() < coreNum) {
                Worker worker = new Worker(task);
                System.out.println("新增worker" + worker);
                workers.add(worker);
                worker.start();
            } else {
//                System.out.println("从消息队列中获取task");
//                blockingQueue.put(task);
                //这里我们换一种方式去向添加阻塞队列中添加任务
                blockingQueue.tryPut(rejectPolicy,task);
            }
        }
    }

 任务队列添加方法

    public void tryPut(RejectPolicy rejectPolicy, T task) {
        lock.lock();
        try {
            if (queue.size() == capacity) {
                //如果满了,需要调用拒绝策略
                rejectPolicy.reject(this,task);
            } else {
                queue.addLast(task);
                emptyWaitSet.signal();
            }
        } finally {
            lock.unlock();
        }
    }

测试 

public class Test {
    public static void main(String[] args) {
        ThreadPool threadPool = new ThreadPool(3,3000,
                TimeUnit.MILLISECONDS,5,
                (queue,task)->{
                    //由调用者决定任务队列满了之后如何处理后续任务,下面是5种处理逻辑,选择一种即可
                    queue.put(task);//死等
                    queue.offer(task,1000,TimeUnit.MILLISECONDS);//超时返回
                                    //啥也不干,直接丢弃任务
                    task.run();//调用者自己执行
                    throw new RuntimeException("任务秩序异常");//抛出异常
                });
        for (int i = 0; i < 10; i++) {
            int j = i;
            threadPool.execute(()->{
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产任务:"+j);
            });
        }
    }
}
  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个自定义的可缓存线程池Java代码示例: ``` import java.util.concurrent.*; public class CustomThreadPool { private ThreadPoolExecutor executor; public CustomThreadPool() { int corePoolSize = 0; int maximumPoolSize = 10; long keepAliveTime = 60L; TimeUnit unit = TimeUnit.SECONDS; BlockingQueue<Runnable> workQueue = new SynchronousQueue<>(); executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } public void execute(Runnable task) { executor.execute(task); } public void shutdown() { executor.shutdown(); } } ``` 在这个例子中,我们使用了Java内置的ThreadPoolExecutor类来创建线程池。这个线程池的特点是: - 初始线程数为0,最大线程数为10,空闲线程超过60秒就会被回收 - 使用SynchronousQueue作为任务队列,这个队列没有容量限制,每个插入操作都必须等待一个相应的删除操作,因此任务会立即被提交到线程池中执行 我们还定义了两个方法,execute和shutdown。execute方法接受一个Runnable任务并将其提交给线程池执行,shutdown方法关闭线程池。使用这个线程池的示例代码如下: ``` public static void main(String[] args) { CustomThreadPool threadPool = new CustomThreadPool(); for (int i = 0; i < 20; i++) { final int taskNum = i; threadPool.execute(() -> { System.out.println("Task " + taskNum + " is running."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Task " + taskNum + " is completed."); }); } threadPool.shutdown(); } ``` 这个示例代码创建了一个CustomThreadPool对象,然后提交了20个任务给线程池执行。每个任务都会打印出自己的编号,然后等待1秒钟,最后打印出自己完成。注意,在最后一行代码中,我们调用了shutdown方法来关闭线程池,确保程序能够正常退出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zmbwcx2003

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值