Jdk多任务执行框架

无限制线程的缺陷

为了提高对多核处理器使用,多线程的操作成了不可替代的利器。但不对线程加以有效管理,线程的创建及销毁都会消耗不必要的性能。

简单的线程池实现

为了在使用过程中,对线程进行一个有效的管理,就引入了线程池的概念–进行线程的复用。

public class ThreadPool {

    private static ThreadPool instance = null;

    /**
     * 空闲线程队列
     */
    private List<PThread> idleThreads;
    /**
     * 已有的线程总数
     */
    private int threadCounter;
    private boolean isShutDown = false;

    public ThreadPool() {
        this.idleThreads = new Vector<>(5);
        threadCounter = 0;
    }

    public int getThreadCounter() {
        return threadCounter;
    }

    public void setThreadCounter(int threadCounter) {
        this.threadCounter = threadCounter;
    }


    /**
     * 取得线程池实例
     * @return
     */
    public synchronized static ThreadPool getInstance() {
        if (instance == null) {
            instance = new ThreadPool();
        }
        return instance;
    }

    /**
     * 将线程放入线程池
     * @param p
     */
    protected synchronized void repool(PThread p) {
        if (!isShutDown) {
            idleThreads.add(p);
        } else {
            p.shutDown();
        }
    }

    /**
     * 停止池中所有线程
     */
    public synchronized void shutDown() {
        isShutDown = true;
        for (int threadIndex = 0; threadIndex < idleThreads.size(); threadIndex++) {
            PThread pThread = idleThreads.get(threadIndex);
            pThread.shutDown();
        }
    }

    /**
     * 执行任务
     * @param target
     */
    public synchronized void start(Runnable target) {
        PThread thread = null;
        /**
         * 如果直接有空闲的线程,则直接使用
         */
        if (idleThreads.size() > 0) {
            int lastIndex = idleThreads.size() - 1;
            thread = idleThreads.get(lastIndex);
            idleThreads.remove(lastIndex);
            thread.setTarget(target);
        } else {
            threadCounter++;
            thread = new PThread(target, "PThread #" + threadCounter, this);
            thread.start();
        }
    }
}

public class PThread extends Thread {                                                          
                                                                                               
    private ThreadPool pool;                                                                   
                                                                                               
    private Runnable target;                                                                   
    private boolean isShutDown = false;                                                        
    private boolean isIdle = false;                                                            
                                                                                               
    public PThread(Runnable target, String name, ThreadPool pool) {                            
        super(name);                                                                           
        this.pool = pool;                                                                      
        this.target = target;                                                                  
    }                                                                                          
                                                                                               
                                                                                               
    public Runnable getTarget() {                                                              
        return target;                                                                         
    }                                                                                          
                                                                                               
    public boolean isIdle() {                                                                  
        return isIdle;                                                                         
    }                                                                                          
                                                                                               
    @Override                                                                                  
    public void run() {                                                                        
        super.run();                                                                           
        while (!isShutDown) {                                                                  
            isIdle = false;                                                                    
            if (target != null) {                                                              
                target.run();                                                                  
            }                                                                                  
                                                                                               
            isIdle = true;                                                                     
            try {                                                                              
                pool.repool(this);                                                             
                synchronized (this) {                                                          
                    wait();                                                                    
                }                                                                              
            } catch (InterruptedException e) {                                                 
                                                                                               
            }                                                                                  
            isIdle = false;                                                                    
        }                                                                                      
    }                                                                                          
                                                                                               
    public synchronized void setTarget(Runnable newTarget) {                                   
        target = newTarget;                                                                    
        /**                                                                                    
         * 设置任务之后,通知run方法,开始执行这个任务                                                             
         */                                                                                    
        notifyAll();                                                                           
    }                                                                                          
                                                                                               
    /**                                                                                        
     * 关闭线程                                                                                    
     */                                                                                        
    public synchronized void shutDown() {                                                      
        isShutDown = true;                                                                     
        notifyAll();                                                                           
    }                                                                                          
}                                                                                              
                                                                                               
public class MyThread implements Runnable {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        int size = 10000;
        long start1 = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            new Thread(new MyThread("noThreadPool" + i)).start();
        }

        System.out.println(System.currentTimeMillis() - start1);

        long start2 = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            ThreadPool.getInstance().start(new MyThread("noThreadPool" + i));
        }

        System.out.println(System.currentTimeMillis() - start2);
    }
}

Executor框架

Executor框架是由jdk提供,在java.util.concurrent包中,

框架结构图
下面是常用的Executors工厂类的主要方法:

  • newFixedThreadPool()方法:该方法返回一个固定的线程数量的线程池,该线程池中的线程数量始终不变。当有一个新任务提交时,线程池中若有空闲则直接执行;若没有,则新任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
  • newSingleThreadExecutor()方法:该方法只产生一个线程的线程池。若有多余任务,被提交到队列中,待线程空闲,按照先入先出的顺序执行队列中的任务。
  • newCachedThreadPool()方法:该方法返回一个可以根据实际情况调整的线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则优先使用可以复用的线程。若所有线程均在工作,又有新任务提交,则创建新线程处理任务。
  • newSingleThreadScheduledExecutor():该方法返回一个ScheduledExecutorService对象,线程池大小为1。ScheduledExecutorService接口在ExecutorService扩展了在给定的时间执行某个任务的功能。
  • newScheduledThreadPool()方法:该方法也返回ScheduledExecutorService对象,但可以指定线程池数量。
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
     public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

自定义线程池

由上面的源码发现,其实线程池实现主要使用了ThreadPoolExecutor类,其构造函数如下:

    /**
     * @param corePoolSize 线程池中的线程数量
     * @param maximumPoolSize 线程池中最大线程数量
     * @param keepAliveTime 当线程池数量超过corePoolSize时,多余的空闲
     *                      线程的存活时间。即,超过corePoolSize的空闲
     *                      线程,在多长时间内会被销毁
     * @param unit keepAliveTime的单位
     * @param workQueue 任务队列,被提交但尚未被执行的任务
     * @param threadFactory 线程工厂,用于创建线程,一般用默认的即可
     * @param handler 拒绝策略。当任务处理不过来时,如何拒绝任务
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  • 参数workQueue 值被提交但未执行的任务队列,他是一个BlockQueue接口的对象,仅用于Runnable接口中。根据功能分类,在ThreadPoolExecutor的构造函数中,有以下几种Blockingqueue:
    • 直接提交的队列:该功能由SynchronousQueue对象提供。SynchronousQueue是一个特殊的BlockingQueue。SynchronousQueue没有容量,每一个插入操作都要等待一个相应的删除操作。SynchronousQueue也不保存任务,它总是把任务提交给线程执行,如果没有空闲的线程,则创建新的线程,如果线程数量已经达到最大值,则执行拒绝策略。
    • 有界的任务队列:有界的任务队列可以使用ArrayBlockingQueue实现。ArrayBlockingQueue的构造函数必须带一个容量参数,表示该队列的最大容量。当队列已满(也创建了最大的线程数),则执行拒绝策略。有界队列当且仅当任务队列满了,才开始创建新线程,直到等于maxumumPoolSize;
    • 无界的任务队列:可以通过LinkedBlockingQueue类实现。无界队列可以无限增长,直到耗尽系统内存为止。
    • 优先任务队列:优先任务队列是带有执行优先级的队列,它通过PriorityBlockingQueue实现。可以控制任务的执行先后顺序,是一个特殊的无界队列,无论有界队列ArrayBlockingQueue,还是未指定大小的无界队列LinkedBlockingQueue都是按照按照先进先出算法处理任务的。而PriorityBlockingQueue可以根据任务自身的优先级顺序先后执行,在确保系统性能的同时,也能有很好的质量保证。
  • 参数handler指定了拒绝策略,即当任务数量超过系统实际承载能力时,该如何处理。Jdk内置了4中拒绝策略,如下图:
    拒绝策略
    • AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
    • CallerRunsPolicy策略:只要线程池未关闭,该策略直接在调用者线程中,运行前被丢弃
    • DiscardOledestPolicy策略:该策略将丢弃最老的一个请求,也就是即将被执行的一个恩物,并尝试再次提交当前任务。
    • DiscardPolicy策略:该策略默默丢弃无法处理的任务,不予任务处理。
    • 自定义策略,只需继承RejectedExecutionHandler

优化线程池大小

《Java》:

 	Ncpu = CPU的数量

  Ucpu = 目标CPU的使用率, 0 <= Ucpu <= 1

  W/C = 等待时间与计算时间的比率

  为保持处理器达到期望的使用率,最优的池的大小等于:

  Nthreads = Ncpu x Ucpu x (1 + W/C)

  你可以使用Runtime来获得CPU的数目:

int N_CPUS = Runtime.getRuntime().availableProcessors();

扩展ThreadPoolExecutor

ThreadPoolExecutor也是一个可扩展的线程池,它提供了beforeExecute()、afterExecute()和terminated()3个接口对线程池进行控制。如下为示例代码:

public class MyThreadPoolExecutor extends ThreadPoolExecutor {
    public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        System.out.println("beforeExecute MyThreadPoolExecutor Name " + ((Thread) r).getName() + " TID :" + t.getId());
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        System.out.println("afterExecute MyThreadPoolExecutor Name " + ((Thread) r).getName() + " TID :" + Thread.currentThread().getId());
        System.out.println("afterExecute  PoolSize = " + this.getPoolSize());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值