Java线程池的理解与使用

简介

线程的创建需要开辟虚拟机栈、本地方法栈、程序计数器等线程私有的内存空间,在线程销毁时需要回收这些系统资源。频繁地创建和销毁线程会浪费大量的系统资源,增加并发编程风险,而且当服务器负载过大的时候,如何让新的线程等待或者友好地拒绝服务?这是线程自身无法解决的。所以需要通过线程池协调多个线程,并实现主次线程隔离、定时执行、周期执行等任务

作用

  • 利用线程池管理并复用线程、控制最大并发数等
  • 实现任务线程队列缓存策略和拒绝机制
  • 实现定时执行、周期执行等与时间相关的功能
  • 隔离线程环境(根据业务独立配置线程池,避免相互影响)

创建

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;
}
  • corePoolSize:常驻核心线程数。如果等于0,则任务执行完成后没有任何请求进入时销毁线程池的线程;如果大于0,即使本地任务执行完成,核心线程也不会被销毁
  • maximumPoolSize:线程池能够容纳同时执行的最大线程数。必须大于等于1,且必须大于等于corePoolSize
  • keepAliveTime:线程池中的线程空闲时间,当空闲时间达到keepAliveTime的值时,线程会被销毁,直到只剩下corePoolSize个线程为止,避免浪费内存和句柄资源。默认情况下,当线程池中的线程数大于corePoolSize时keepAliveTime才会起作用,但是当java.util.concurrent.ThreadPoolExecutor#allowCoreThreadTimeOut变量设置为true时,核心线程超时后也会被回收
  • unit:时间单位。keepAliveTime的时间单位通常是java.util.concurrent.TimeUnit#SECONDS
  • workQueue:缓存队列。当请求的线程数大于corePoolSize时,线程进入BlockingQueue阻塞队列,BlockingQueue队列缓存达到上限后,如果还有新任务需要处理,那么线程池会创建新的线程,最大线程数为maximumPoolSize
  • threadFactory:线程工厂。用来生产一组相同任务的线程
  • handler:执行拒绝策略的对象。当workQueue的任务缓存区到达上限后,并且活动线程数达到maximumPoolSize时,线程池通过该策略处理请求

拒绝

  • CallerRunsPolicy:提交任务的线程自己执行该任务
  • AbortPolicy:直接抛出异常(throws RejectedExecutionException)
  • DiscardPolicy:直接丢弃任务
  • DiscardOldestPolicy:丢弃最早进入队列的任务,接收新任务

定制

自定义ThreadFactory:给线程赋予一个有意义的名字

class UserThreadFactory implements ThreadFactory {
    private static String namePrefix;
    private static ThreadGroup threadGroup;
    private static AtomicInteger nextId = new AtomicInteger(1);

    public UserThreadFactory(String whatFeatureOfGroup) {
        namePrefix = "UserThreadFactory's " + whatFeatureOfGroup + "-Worker-";
        final SecurityManager securityManager = System.getSecurityManager();
        threadGroup = Objects.nonNull(securityManager) ? securityManager.getThreadGroup() 
                : Thread.currentThread().getThreadGroup();
    }

    @Override
    public Thread newThread(Runnable task) {
        String name = namePrefix + nextId.getAndIncrement();
        return new Thread(threadGroup, task, name, 0);
    }
}

自定义拒绝策略

class UserRejectedExecutionHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
     	// todo 自定义的策略
        log.warn("Task rejected. Executor Info:{}", executor.toString());        
    }
}

注意

1,为什么不推荐使用Executors快速创建线程池?因为Executors 提供的很多方法默认使用的都是无界的 LinkedBlockingQueue,高负载情境下,无界队列很容易导致 OOM
2,谨慎使用默认拒绝策略。线程池默认的拒绝策略会直接抛出运行时异常(throw RejectedExecutionException),建议自定义拒绝策略

发布了22 篇原创文章 · 获赞 17 · 访问量 841

猜你喜欢

转载自blog.csdn.net/momo57l/article/details/104415754
今日推荐