您的位置 首页 java

Java面试篇基础部分-Java线程池工作原理

线程池 的出现,主要是用来管理一组线程的工作运行状态,这样可以方便JVM更好的利用CPU资源。

Java线程池的工作原理:JVM先根据用户的参数创建一定数量的可运行的线程任务,并且将这些任务放入到队列中,在线程创建之后,启动这些任务。

如果当线程数超过最大线程数,这个线程数是由用户在创建线程池的时候进行设计的。对于超过数量的线程来说进行等候排队,等待其他线程执行完毕之后,线程池会发现可用的线程,进行再次从队列中获取到任务并执行。

线程池的主要作用是线程复用、线程资源管理、控制操作系统的最大并发数量,从而保证系统的高效运行,这个高效运行时通过线程池内部的资源重复利用来实现。并且通过控制最大并发数保证系统资源的合理利用。

线程的复用

如果我们对于 线程 进行抽象的话,那么每个 thread 类都有一个start()方法,在程序调用到start方法之后线程启动,JVM会调用这个类的run方法。之前提到过,Thread类的run方法其实调用了Runnbale的run方法。所以,继承了Thread类,在start方法中不断循环调用传入的Runnable对象,程序就不断的执行run方法中的代码。

这样的话可以将方法中的Runnbale对象,放入到一个Runnbale的队列中,当线程在获取到下一个Runnbale对象的时候可以进行阻塞,这样可以控制正在执行的线程的数量,也可以有效的保证系统中的线程等待其他正确的线程执行完毕,这样的一个机制就是线程池,可以对队列以及创建的线程资源进行重复的利用。多个Runnbale可以被一个Thread对象重复调用start方法来执行。

线程池的核心组件和核心类

Java中的线程池有如下的几个核心组件组成

  • 线程池管理器:用于创建并且管理线程池
  • 工作线程:线程池中正在执行的具体的任务的线程
  • 任务接口:用来定义工作线程如何调度,如何执行,线程实现了这个接口,才能被线程池认可,进入线程池的管理调度。
  • 任务队列:用来存放需要处理的等待任务,新任务会不断地进入到队列中进行等待,执行完成之后任务会被从队列中移除

Java中线程池机制是通过Executor框架进行实现的

在框架中用到Executor、Executors、ExecutorService、ThreadPoolExecutor、Callable、Future、FutureTask一些核心类。

其中, ThreadPoolExecutor 是用来构建线程的核心,代码如下

     public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }  

这个类的 构造方法 有如下几个参数

  • corePoolSize:线程池中核心线程的数量
  • maximumPoolSize:线程池中最大线程的数量
  • keepAliveTime:当前线程数量超过corePoolSize的时候,空闲线程存活时间
  • unit:keepAliveTime的 时间单位
  • workQueue:任务队列,已经提交但未被执行的任务存放的空间
  • threadFactory:线程工厂,用于创建线程。
  • handler:用来处理任务过多或者其他的原因导致线程池无法处理的时候,任务的拒绝策略。

线程池的 工作流程

Java线程池工作流程:线程被创建的时候,向系统申请一个用于执行线程队列和管理线程的资源,然后调用execute()添加任务会按照下面的流程执行任务

  • 1、如果正在运行的线程数量少于corePoolSize的数量,线程池就会立即创建并执行这个线程任务。
  • 2、如果正在运行的线程数量大于等于corePoolSize的数量,那么这个任务就会被添加到队列中。
  • 3、如果队列满了并且正在运行的线程数量少于maximumPoolSize的数量的时候,线程池就会创建非核心线程执行任务。
  • 4、如果队列已经满了并且正在运行的线程数量大于或者等于maximumPoolSize的数量时,线程队列执行并且抛出RejectedExecutionHandler的异常
  • 5、在线程任务执行完毕之后,线程任务将会被从线程队列中移除,线程池将会获取下一个任务继续进行。
  • 6、在线程处于空闲状态,并且时间超过了keepAliveTime时间的时候,正在运行的线程数量超过corePoolSize的数量,这个线程就会被认定空闲线程并停止,所以在线程池中所有的线程任务执行完毕之后,线程池会收缩到corePoolSize的大小

拒绝策略

我们都知道,如果在线程池中核心线程被用完并且队列已经满了,这个时候线程池的资源已经耗尽了,线程池将没有足够的线程资源执行新的任务的时候,为了保证线程的安全性这个时候就会有拒绝策略的出现,也就是说禁止线程提交。

JDK 中内置了几个拒绝策略,分别是AbortPollicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy。当然还可以实现自定义的拒绝策略。

AbortPolicy

直接抛出异常,阻止线程正常运行。

   /**
     * A handler for rejected tasks that throws a
     * {@code RejectedExecution Exception }.
     */    public  static  class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */        public  void  rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }
  

CallerRunsPolicy

如果被丢弃的线程任务没有关闭,则执行该线程任务。需要注意的是,CallerRunsPolicy拒绝策略不会真的丢弃任务。

  /**
     * A handler for rejected tasks that runs the rejected task
     * directly in the calling thread of the {@code execute} method,
     * unless the executor has been shut  down , in which case the task
     * is discarded.
     */    public static class CallerRunsPolicy implements  Reject edExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
         */        public CallerRunsPolicy() { }

        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }  

DiscardOldestPolicy

移除线程队列中最早的一个线程任务,并且尝试提交当前任务。

 /**
     * A handler for rejected tasks that discards the oldest unhandled
     *  request  and then retries {@code execute}, unless the executor
     * is shut down, in which case the task is discarded.
     */    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }  

DiscardPolicy

丢弃当前的线程任务而不做任何的处理。如果系统允许在资源不足的情况下丢弃部分的任务,这是保证系统安全的一种很好的解决方案。

     /**
     * A handler for rejected tasks that silently discards the
     * rejected task.
     */    public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */        public DiscardPolicy() { }

        /**
         * Does  nothing , which has the effect of  discarding  task r.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }  

自定义的拒绝策略

可以看到以上的四种策略都是继承了RejectedExecutionHandler接口,如果无法满足实际的使用需求,开发者可以通过自己实现RejectedExecutionHandler的接口来实现自定义的拒绝策略,并且定义自定义的异常来处理拒绝之后的内容。

     private  static class RejectHandler implements RejectedExecutionHandler {
     private int  discardNumber = 5;
        private List<Runnable> discardList = new ArrayList<Runnable>();
       
        private RejectHandler(int discardNumber) {
        this.discardNumber = discardNumber;
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                if(e.getQueue().size()>discardNumber){
                        // 批量移除线程队列中的指定数量的线程
                e.getQueue().drainTo(discardList,discardNumber);
                        discardList.clear();// 清空discardList列表
                        if(!e.isShutdown()){
                        e.execute(r)  // 尝试提交当前任务
                        }
                }
        }
    }  

文章来源:智云一二三科技

文章标题:Java面试篇基础部分-Java线程池工作原理

文章地址:https://www.zhihuclub.com/180676.shtml

关于作者: 智云科技

热门文章

网站地图