Java 线程池的缺陷

一.缺陷

1.非核心线程的创建时机

            1.1) 核心线程的数量是 corePoolSize 的值,非核心线程的数量是 maxinumPoolSize - corePoolSize ;

            1.2) 非核心线程创建的触发时机是:当前线程池中核心线程已满,且没有空闲的线程,还有任务等待队列已满,

满足上面的所有条件,才会去创建线程去执行新提交的任务;

           1.3) 如果线程池中的线程数量达到 maxinumPoolSize 的值,此时还有任务进来,就会执行拒绝策略,抛弃任务或者其他

如果拒绝策略是抛弃任务的话,有一种场景,就会造成大量任务的丢弃,就是瞬时冲高的情况下。

2.排队任务调度策略

          当线程池中核心线程数量已达标,且没有空闲线的情况下,在产生的任务,会加入到等待队列中去,这样一直持续下去,

等到等待队列已满,在来的任务,会创建非核心线程去执行新提交的任务,那么就产生一种结果,在等待队列中的任务是先提

交的任务,反而没有在此时提交的任务先执行。

任务的执行顺序和任务的提交顺序不一致,如果业务需求的任务是有先后依赖关系的,就会降低线程的调度效率



 二.优化策略

   1. 非核心线程的创建时机

        Java线程池的调度策略是,当等待队列已满,才会去创建分核心线程去执行新的任务

        优化方案:是自定义一个等待队列,当队列的值达到一定的阈值,就开始创建分核心线程去执行,

而不是等到队列已满,才去创建

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 当插入队列workQueue.offer(command)失败时,才会去创建非核心线程
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

     1.1) 自定义等待队列,采用LinkedBlockingQueue作为基本队列,产生一个双生产者,单消费者的队列,将

生产者分为普通生产者,和超限生产者

  • 普通生产者:使用BlockingQueue原生的offer等接口添加任务,可用队列大小为 queueSize*threshold,Java

原生线程池作为生产者调用该组接口

  • 超限生产者:使用自定义的additionOffer接口添加任务,可用队列大小为 queueSize*(1-threshold),我们复写

的线程池框架负责执行次动作

package com.roger.threadpool.queue;

import java.util.Collection;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 *  定制双生产者,单消费者队列
 *  通过阈值叫队列分为普通生产队列和超限生产队列
 *      普通生产队列通过Java原生的线程池使用
 *      超限生产队列通过自定义的线程池使用
 * @param <T>
 */
public class CustomThreadBlockingQueue<T> extends LinkedBlockingQueue<T> {

    private int capacity;
    private int additionalCapacity;

    // 处理原则:capacity,additionalCapacity生产的时候分开,消费的时候一起消费
    public CustomThreadBlockingQueue(int queueSize,double threshold) {
        super(queueSize);

        this.capacity = new Double(queueSize * threshold).intValue();
        this.additionalCapacity = queueSize - this.capacity;
    }

    @Override
    public boolean addAll(Collection<? extends T> c){
        throw new UnsupportedOperationException();
    }

    //普通生产者接口
    @Override
    public void put(T e) throws InterruptedException {
        throw new UnsupportedOperationException();
    }

    //普通生产者接口
    @Override
    public boolean add(T e) {
        if (super.size() >= capacity) {
            throw new IllegalStateException();
        }

        //并发下不足够精确
        return super.add(e);
    }

    //普通生产者
    @Override
    public boolean offer(T t) {
        //并发下不足够精确
        if (super.size() >= capacity) {
            return false;
        }

        return super.offer(t);
    }

    @Override
    public boolean offer(T t, long timeout, TimeUnit unit) throws InterruptedException {
        //并发下不足够精确
        if (super.size() >= capacity) {
            return false;
        }
        return super.offer(t, timeout, unit);
    }

    //符合LVS替换原则,弱化为BlockingQueue时为标准的单生产者、消费者队列
    @Override
    public int remainingCapacity() {
        int remain = super.remainingCapacity() - additionalCapacity;
        return remain >= 0 ? remain : 0;
    }

    //超限生产者接口
    public boolean additionalOffer(T e) {
        return super.offer(e);
    }
}

    1.2) 上面的队列中,普通生产者是通过java原生的线程池去使用,但是超限生产者,需要自定义的线程池去使用

package com.roger.threadpool.queue;

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CustomThreadPoolExecutor extends ThreadPoolExecutor {


    public CustomThreadPoolExecutor(int poolSize, int maxSize, int keepAliveTime, TimeUnit timeUnit, int queueSize, double threshold, RejectedExecutionHandler handler) {
        super(poolSize, maxSize, keepAliveTime, timeUnit,
                new CustomThreadBlockingQueue<Runnable>(queueSize, threshold));

        super.setRejectedExecutionHandler(new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                CustomThreadBlockingQueue<Runnable> blockingQueue = (CustomThreadBlockingQueue<Runnable>) executor.getQueue();
                // 如果超限任务添加成功,则创建非核心线程去执行任务
                if(!blockingQueue.additionalOffer(r)){

                    //如果超限任务添加失败,转由外部处理
                    handler.rejectedExecution(r,executor);
                }
            }
        });
    }

}

2.任务调度策略   -  希望任务调度是有序的

    基本思路:

          2.1) 采用任务代理类,将任务绑定时机延迟到任务执行时,而不是添加时

          2.2) 增加新的任务队列,按添加顺序保存真正的执行任务

          2.3) 运行时,动态从新增任务队列中获取头部任务,做到FIFO

package com.roger.threadpool;

import java.util.concurrent.BlockingQueue;

public class RunTaskProxy implements Runnable {

    private BlockingQueue<Runnable> realTaskQueue = null;

    public RunTaskProxy(BlockingQueue<Runnable> realTaskQueue) {
        this.realTaskQueue = realTaskQueue;
    }

    @Override
    public void run() {
        //运行时绑定,头部获取任务,保证FIFO
        Runnable runnable = realTaskQueue.remove();
        runnable.run();
    }
}
package com.roger.threadpool.queue;

import java.util.concurrent.*;

public class CustomThreadPoolExecutor extends ThreadPoolExecutor {

    //真正任务保序队列
    private BlockingQueue<Runnable> realTaskQueue = new LinkedBlockingQueue<>();

    public CustomThreadPoolExecutor(int poolSize, int maxSize, int keepAliveTime, TimeUnit timeUnit, int queueSize, double threshold, RejectedExecutionHandler handler) {
        super(poolSize, maxSize, keepAliveTime, timeUnit,
                new CustomThreadBlockingQueue<Runnable>(queueSize, threshold));

        super.setRejectedExecutionHandler(new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                CustomThreadBlockingQueue<Runnable> blockingQueue = (CustomThreadBlockingQueue<Runnable>) executor.getQueue();
                // 如果超限任务添加成功,则创建非核心线程去执行任务
                if(!blockingQueue.additionalOffer(r)){

                    //移除任务,任务真正拒绝
                    realTaskQueue.remove(r);

                    //如果超限任务添加失败,转由外部处理
                    RejectedExecutionHandler handler = executor.getRejectedExecutionHandler();
                    handler.rejectedExecution(r,executor);
                }
            }
        });
    }

    @Override
    public void execute(Runnable command) {
        // 任务记录
        realTaskQueue.add(command);
        // 封装代理对象
        super.execute(new RunTaskProxy(realTaskQueue));
    }
}

 

转载自简书:https://www.jianshu.com/p/896b8e18501b

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值