JUC 线程池

概述

  1. 线程池的作用:节省资源、提升响应、削峰限流、管理线程

  2. ThreadPoolExecutor的核心参数:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler

  3. Executors静态工厂类,提供常用的4种线程池:

  • FixedThreadPool,固定线程数量的线程池,配合一个无界的LinkedBlockingQueue
  • SingleThreadPool,固定使用一个线程来工作,但是可以无限堆积,因为是单个线程,所以它可以保证认为是按顺序执行的,因为LinkedBlockingQueue是严格的FIFO的
  • CachedThreadPool,同步线程池,默认非公平的,基于SynchronousQueue队列
  • ScheduledThreadPool,指定核心线程数的定时执行线程池,使用时通过 pool.schedule调用,指定延迟时间,依赖DelayedWorkQueue来实现
  1. 线程池使用的优先级: 基本线程池 > BlockingQueue >最大线程池 >饱和策略

  2. 线程池支持的函数

  • execute 不会返回线程执行是否成功
  • submit 返回一个future对象可以阻塞获取返回值
  • shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

类的继承关系

http://cmsblogs.com/?p=2444

  1. Executor作为顶层接口,只提供一个execute()接口方法
  2. ThreadPoolExecutor继承AbstractExecutorService抽象类-->ExcutorService接口-->Executor顶层接口
  3. Executors静态工厂类,提供了很多的通用线程池的静态方法

线程池的作用

  1. 节省资源:复用线程,减少重复创建和销毁线程
  2. 提升响应速度:如果有可以复用的线程,不需要创建新线程,直接执行业务操作,提升速度
  3. 方便管理,提供了线程池的shutdown、shutdownnow、isTerminated、isShutDown等方法
  4. 削峰限流,不让一下子过多的线程把程序搞崩,比如RocketMQ里面的Consumer就会把获取到的消息放入到线程池里来慢慢消费,Dubbo也有一个默认100长度的LinkedBlockQueue
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

使用规则

优先级: 基本线程池 > BlockingQueue >最大线程池 >饱和策略

  1. 线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务,即使有空闲的已经创建的线程也不会复用。满了,则进入下个流程。
  2. 线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程
  3. 线程池判断整个线程池是否已满?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务

runnableTaskQueue

常用阻塞队列特性总结:通过加锁实现安全地读写(size、contains函数也是加锁读),通过condition或者AtomicInteger来协调生产和消费

  1. ArrayBlockQueue:1把锁,无法并发写,2个condition,通过await、signal来实现线程调度。FIFO队列的定长阻塞队列
  2. LinkedBlockQueue:2把锁,支持生产、消费并发执行,通过AtomicInteger和CAS来控制库存。可以指定大小,默认无界队列。
  3. SynchronousQueue:没有容量的队列,put、take成为一对儿才可以执行,否则阻塞,add和remove如果没有配对成功,直接报错。用于快速响应的业务场景
  4. PriorityBlockingQueue:有优先级的队列,插入元素实现Compare接口,内部维护了一个最小堆,一把锁,基于数组,自动扩容的无界队列

RejectedExecutionHandler(饱和策略)

  • AbortPolicy,默认饱和策略,直接抛出异常
  • CallerRunsPolicy:只用调用者所在线程来运行任务
  • DiscardPolicy:不处理,丢弃掉,也不会日志什么的
  • DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  • 自定义Police,需要实现RejectedExecutionHandler

线程池的使用 execute submit

execute 不会返回线程执行是否成功

threadsPool.execute(new Runnable() {});

submit 返回一个future对象可以阻塞获取返回值

Future future=threadsPool.submit(new Runnable() {});
Boolean result= future.get();

线程池的关闭

shutdown或shutdownNow方法来关闭线程池,但是它们的实现原理不同,

  • shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
  • shutdownNow的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。shutdownNow会首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。

Callable &Future &FutureTask

http://blog.csdn.net/javazejian/article/details/50896505

Callable接口

  • 加强版的Runnable,有返回值,会抛出异常
public interface Runnable {  
    public abstract void run();  
}  
public interface Callable {   
      V   call()   throws Exception;   
}  
  • 可以作为参数,被线程池调用执行,Future submit(Callable task);

Future接口

作为异步计算的顶层接口,Future对具体的Runnable或者Callable任务提供了三种操作:

  • 执行任务的取消:cancel()
  • 查询任务的状态: isDone()、isCancelled()
  • 获取任务的执行结果(只有Callable任务才有,而且是阻塞):get()、V get(Long timeout , TimeUnit unit)

FutureTask类(同时实现了Runnable、Future)

  • 既可以作为一个Runnable实现类在线程池中执行,甚至可以直接new Thread(futureTask).start();也可以作为一个future获取其他线程的执行结果和状态,
  • done():异步计算完成后调用的回调函数

数据库连接池原理

  • 解决的问题:频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。解决方案:连接复用。
  • 大部分连接池可以设置最大连接数,最小连接数,最大闲置时间等参数,连接池根据这些参数,新建和关闭连接

使用案例

Executors 类里面实现了一些常用的工具方法,返回适合不同业务场景的线程池实现类。
但是要注意FixedThreadPool和SingleThreadPool都是定长的线程池,但是排队队列都是无限长的;CachedThreadPool和ScheduledThreadPool都是可以启动无数个线程来完成功能,都要注意不能OOM了。

  1. FixedThreadPool,固定线程数量的线程池,配合一个无界的LinkedBlockingQueue,重复复用指定数量的coresize的线程
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }
  1. SingleThreadPool,固定使用一个线程来工作,但是可以无限堆积,因为是单个线程,所以它可以保证认为是按顺序执行的,因为LinkedBlockingQueue是严格的FIFO的
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue()));
    }
  1. CachedThreadPool,同步线程池,默认非公平的
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }
  1. ScheduledThreadPool,指定核心线程数的定时执行线程池,使用时通过 pool.schedule调用,指定延迟时间,依赖DelayedWorkQueue来实现
 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue());
    }



ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);

        for (int i = 0; i < 5; i++) {
            Future result = pool.schedule(new Callable() {

                @Override
                public Integer call() throws Exception {
                    int num = new Random().nextInt(100);//生成随机数
                    System.out.println(Thread.currentThread().getName() + " : " + num);
                    return num;
                }
            }, 3, TimeUnit.SECONDS);


            System.out.println(result.get());
        }

        pool.shutdown();

数据库的连接池管理

https://www.cnblogs.com/happySmily/p/5941813.html

常见的数据库连接池:dbcp、c3p0、druid | weblogic(收费的)
常见参数:

  1. initialSize=10: 程序一启动就自动创建 N个数据库长连接
  2. timeBetweenEvictionRunsMillis = "30000" ,程序启动后,每隔30scheck一下当前线程数是否合格:A)Idle线程是否超时,超时就回收掉 B)当前可用线程是否大于minIdle,小于就自动创建,如果设置为非正数,则不运行空闲连接回收器线程,默认是-1,不启动单独线程维护minIdle
  3. maxIdle:当可用线程>minIdle但是小于maxIdle的时候,空闲过期就会被回收
  4. maxActive:当可用线程超过maxIdle的,小于maxActive的时候,使用完就立刻回收
  5. maxWait=3000; 这个指的是最大等待时间,如果超过3秒还未分配到连接,就报错
maxWait="3000"    从池中取连接的最大等待时间,单位ms.  
initialSize="10"  初始化连接    
minIdle="10"   最小空闲连接  
maxIdle="60"   最大空闲连接
maxActive="80" 最大活动连接  

validationQuery = "SELECT 1"  验证使用的SQL语句  
testWhileIdle = "true"      指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.  
testOnBorrow = "false"   借出连接时不要测试,否则很影响性能  
timeBetweenEvictionRunsMillis = "30000"  每30秒运行一次空闲连接回收器  
minEvictableIdleTimeMillis = "1800000"  池中的连接空闲30分钟后被回收  
numTestsPerEvictionRun="10" 在每次空闲连接回收器线程(如果有)运行时检查的连接数量  
      
removeAbandoned="true"  连接泄漏回收参数,当可用连接数少于3个时才执行  
removeAbandonedTimeout="180"  连接泄漏回收参数,180秒,泄露的连接可以被删除的超时值  

参考资料

大牛博客:宏观上分析 线程池
http://ifeve.com/java-threadpool/

CSDN系列专栏简易
http://blog.csdn.net/column/details/javathreadpool.html

死磕java并发系列有4篇线程池相关的
http://cmsblogs.com/?p=2122

你可能感兴趣的:(JUC 线程池)