java线程池的原理研究(二)

电脑杂谈  发布时间:2020-05-10 01:17:30  来源:网络整理

java线程池实例_java线程池执行原理_java 线程池

上面继续: Java线程池的原理研究

ThreadPoolExecutor,线程池类,继承自AbstractExecutorService

public class ThreadPoolExecutor extends AbstractExecutorService 

ThreadPoolExecutor提供了四种构造方法(这里仅介绍一种):

 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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

有必要解释每个参数:

为了便于在大量上下文中使用,此类提供了许多可调整的参数和扩展挂钩. jdk文档建议在正常情况下,使用执行程序提供的工厂方法配置,即提供良好的线程池. 如果要手动配置,则需要遵循以下规则:

java线程池执行原理_java 线程池_java线程池实例

ThreadPoolExecutor将根据corePoolSize和maximumPoolSize设置的边界自动调整池大小. 在执行方法(java.lang.Runnable)中提交新任务时:

1. 正在运行的线程小于corePoolSize,然后即使其他辅助线程都处于空闲状态,也要创建一个新线程来处理请求.

2: 运行比corePoolSize多且小于maximumPoolSize的线程,然后将任务放入队列,空闲线程将从队列中取出任务,并且仅在队列已满时创建新线程.

3: 如果设置的corePoolSize和maximumPoolSize相同,则会创建固定大小的线程池.

4: 如果将maximumPoolSize设置为基本的无界值(例如Integer.MAX_VALUE),则允许该池适应任何数量的并发任务.

请注意以下两点:

在大多数情况下,仅根据构造函数设置核心和最大池大小,但是您也可以使用setCorePoolSize(int)和setMaximumPoolSize(int)进行动态更改.

java 线程池_java线程池实例_java线程池执行原理

当池中的线程数大于corePoolSize时,多余的线程将长时间等待keepAliveTime,如果没有处理请求,则销毁它们自己.

使用ThreadFactory创建新线程. 除非另有说明,否则Executors.defaultThreadFactory()用于在同一ThreadGroup中创建线程,并且这些线程具有相同的NORM_PRIORITY优先级和非守护程序状态. 通过提供不同的ThreadFactory,可以更改线程名称,线程组,优先级,守护程序状态等. 如果从newThread返回null时ThreadFactory无法创建线程,则执行程序将继续运行,但无法执行任何任务.

ThreadFactory是线程工厂,它是一个接口:

public interface ThreadFactory {
    Thread newThread(Runnable r);
}

ThreadPoolExecutor中的threadFactory由Executors工具类提供:

public static ThreadFactory defaultThreadFactory() {
        return new DefaultThreadFactory();
}

static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }
        public Thread newThread(Runnable r) {
           ////创建的线程以“pool-N-thread-M”命名,N是该工厂的序号,M是线程号
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0); 
            //设为非后台线程                      
            if (t.isDaemon())
                t.setDaemon(false);
            //优先级为normal    
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

java线程池实例_java 线程池_java线程池执行原理

DefaultThreadFactory是静态内部类

如前所述,当线程池中运行的线程等于或大于corePoolSize时,Executor总是更喜欢将请求添加到队列中而不添加新线程. 有三种将任务添加到队列的策略(有关详细信息,请参见jdk文档).

在两种情况下,新提交的任务将被拒绝:

执行器关闭时

执行器使用有限的边界来获得最大的线程和工作队列容量,并且已经饱和

对于被拒绝的任务,execute方法将调用其RejectedExecutionHandler的RejectedExecutionHandler.rejectedExecution(java.lang.Runnable,java.util.concurrent.ThreadPoolExecutor)方法. 以下提供了四种预定义的处理程序策略:

在默认的ThreadPoolExecutor.AbortPolicy中,被拒绝的处理程序将抛出运行时RejectedExecutionException.

java线程池执行原理_java 线程池_java线程池实例

在ThreadPoolExecutor.CallerRunsPolicy中,线程调用自身执行运行任务. 该策略提供了一种简单的反馈控制机制,可以降低新任务的提交速度.

在ThreadPoolExecutor.DiscardPolicy中java线程池执行原理,将删除无法执行的任务.

在ThreadPoolExecutor.DiscardOldestPolicy中java线程池执行原理,如果执行器尚未关闭,则将删除工作队列开头的任务,并将重试执行器(如果再次失败,请重复此过程).

也可以定义和使用其他种类的RejectedExecutionHandler类,但这需要格外小心,尤其是当该策略仅用于特定容量或排队策略时.

此类提供了两个受保护和可重写的钩子方法:

protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }

在每个任务之前和之后调用这两个方法. 它们可以用来操纵执行环境. 请注意,这里是每个任务,每次运行新任务时都会执行. 例如,重新初始化ThreadLocal,收集统计信息或添加日志条目. 此外,还可以重写终止方法(),以执行执行程序完全终止后需要执行的所有特殊处理.

如果钩子或回调方法引发异常,则内部辅助线程将依次失败并突然终止.

jdk文档提供了可以暂停和恢复的线程池的示例:

class PausableThreadPoolExecutor extends ThreadPoolExecutor {
   private boolean isPaused;
   private ReentrantLock pauseLock = new ReentrantLock();
   private Condition unpaused = pauseLock.newCondition();
   public PausableThreadPoolExecutor(...) { super(...); }
   protected void beforeExecute(Thread t, Runnable r) {
     super.beforeExecute(t, r);
     pauseLock.lock();
     try {
       while (isPaused) unpaused.await();
     } catch(InterruptedException ie) {
       t.interrupt();
     } finally {
       pauseLock.unlock();
     }
   }
   public void pause() {
     pauseLock.lock();
     try {
       isPaused = true;
     } finally {
       pauseLock.unlock();
     }
   }
   public void resume() {
     pauseLock.lock();
     try {
       isPaused = false;
       unpaused.signalAll();
     } finally {
       pauseLock.unlock();
     }
   }
 }

java线程池的原理学习(三)


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/ruanjian/article-204656-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...