浅谈--java线程池

一、为什么使用线程池

线程是一个操作系统概念。操作系统负责这个线程的创建、挂起、运行、阻塞和终结操作。而操作系统创建线程、切换线程状态、终结线程都要进行CPU调度——这是一个耗费时间和系统资源的事情。 
另一方面,大多数实际场景中是这样的:处理某一次请求的时间是非常短暂的,但是请求数量是巨大的。这种技术背景下,如果我们为每一个请求都单独创建一个线程,那么物理机的所有资源基本上都被操作系统创建线程、切换线程状态、销毁线程这些操作所占用,用于业务请求处理的资源反而减少了。所以最理想的处理方式是,将处理请求的线程数量控制在一个范围,既保证后续的请求不会等待太长时间,又保证物理机将足够的资源用于请求处理本身。 
另外,一些操作系统是有最大线程数量限制的。当运行的线程数量逼近这个值的时候,操作系统会变得不稳定。这也是我们要限制线程数量的原因。

二、线程池基本用法

JAVA语言为我们提供了两种基础线程池的选择:ScheduledThreadPoolExecutor和ThreadPoolExecutor,ScheduledThreadPoolExecutor又是ThreadPoolExecutor的子类。他们都实现Executor、ExecutorService接口。

 

1、Executor:执行已提交的Runnable任务的对象,并提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用Executor而不是显式地创建线程。例如,可能会使用一下方法,而不是为一组任务中每个任务调用new Thread(new RunnableTask()).start();

Executor excutor = anExecutor;

excutor.excute(new RunnableTask1());

excutor.excute(new RunnableTask2());

 

1.1、不过Executor接口并没有严格地要求执行是异步的,在最简单的情况下,执行程序可以在调用的线程中立即运行已提交任务;

class DirectExucutor implements Executor{
public void execute(Runnable r){
             r.run();

         }

}

1.2、更常见的是,任务是在某个不是调用者线程的线程中执行的。

class ThreadPerTaskExecutor implements Executor{

     public void execute(Runnable r){

        new Thread(r).start();

    }

}

1.3、Executor实现都调度任务的方式和时间强加了某种限制。以下执行程序使任务提交与第二个执行程序保持连续,复合执行程序

class SerialExecutor implements Executor{

 final Queue<Runnable> mTasks = new ArrayDeque<Runnable>();

final Executor mExecutor;

    Runnable active;

     SerialExecutor(Executor executor){

           this.mExecutor = executor;

   }
  

 public synchronized void execute (final Runnable r){

        mTasks .offer(new Runnable(){

             public void run(){

         try{

  r.run();

}finally{

scheduleNext();

}

            }

         });

        if(active == null){

              scheduleNext();

         }

    }

protected synchronized void scheduleNext(){

if((active = mTasks.poll()) != null){

mExecutor.excute(avtive);

}

}

}

2、ExecutorService:目前使用最为广泛的接口,他同时继承Executor的接口(ps:后面重点讲解)。

 

三、ThreadPoolExecutor 

1、构造方法: 

ThreadPoolExecutor mTexecutor = new ThreadPoolExecutor(int corePoolSize,int maximunPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,RejectedExecutionHandler headler)

 

corePoolSize:线程池中核心线程最大值;如果不进行特别的设定,线程池中始终会保持corePoolSize数量的线程数(不包括创建阶段)。线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程,默认情况下会一直存活在线程池中

maximunPoolSize:线程池中线程总数最大值(线程总数 = 核心线程 + 非核心线程)。如果设置corePoolSize参数和设置maximumPoolSzie参数一致,线程池在任何情况下都不会回收空闲线程,keepAliveTime和TimeUnit也就失去了意义。

keepAliveTime:该线程池中非核心线程闲置的时间超过keepAliveTine就会被回收,如果allowCoreThreadTimeOut = true,则会作用核心线程。

TimeUnit:keepAlvieTime的单位,TimeUnit是一个枚举类型。

BlockingQueue<Runnable>:线程池中的任务队列,维护着等待执行的Runnable对象,当所有核心线程都处于非闲置时,新添加的任务会被添加到任务队列中等待处理,如果队列满了,则新建非核心线程执行任务;

常见workQueue类型:

①、SynchronousQueue:一种阻塞队列,其中每个插入操作必须等待另一个线程的移除操作(实现生产者/消费者模型)。

②、ArrayBlockingQueue:由数组支持的有界阻塞队列,此队列按照FIFO(先进先出)原则对元素进行排序;这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建这样的缓存区,就不能再增加其容量。如果试图加入已满的队列,则会导致阻塞;试图从空的队列中提取元素将会导致阻塞。

③、LinkedBlockingQueue:是我们在ThreadPoolExecutor线程池中常用的等待队列,实现是线程安全,实现FIFO的特性,作为生产者消费者首先。它可以指定容量也可以不指定容量。由于它具有“无限容量”的特性,所以我们将它归入无限队列的范畴(实际上任何无限容量的队列/栈都是有容量的,这个容量就是Integer.MAX_VALUE)。它实现是基于链表结构,当队列接受任务时,当前线程数小于核心线程数,则新建线程(核心线程)执行任务;如果等于核心线程数,则进入队列等待。

④、DelayQueue:一种无界阻塞队列,只有延迟期满时,才能执行任务。它是有序的,队头对象延迟到期最长时间,头队列不能为null。

 

ThreadFactory:创建线程的方式,这是一个接口。

 

RejectedExecutionHandler :无法由ThreadPoolExecutor执行的任务的处理。抛出异常专用。①、③会由Handler抛出异常。

 

2、ThreadPoolExcutor策略(当一个任务被添加到线程池时):

①、线程数量未达到corePoolSize,则新建一个核心线程执行;

②、线程数量达到corePoolSize,则将任务加入队列等待;

③、队列已满,新建非核心线程执行任务

④、队列已满,总线线程数有达到maximumPoolSize,就RejectedExecutionHeadler异常抛出。

 

四、常见四种线程池

1、CacheThreadPool:可缓存线程池

创建方法:

ExecutorService cacheThreadPool = Executors.newCacheThreadPool();

特性:

①、线程数无限制

②、有空闲线程则复用空闲,如无空闲线程则新建线程 

③、一定程度减少频繁创建/销毁线程,减少系统开销。

 

2、 FiexdThreadPool:定长线程池

创建方法:

①、ExecutorService fiexdThreadPool= Executors.newFiexdThreadPool(int nThreads);

②、ExecutorService fiexdThreadPool= Executors.newFiexdThreadPool(int nThreads,ThreadFactory threadFactory); 

特性:

①、可控制线程最大并发量(同时执行的线程数)

②、超出的线程会在队列中等待

 

3、ScheduleThreadPool:定长线程池

创建方法:

ExecutorService scheduleThreadPool= Executors.newScheduleThreadPool(int corePoolSize);

特性:

①、支持定时及周期性任务执行。

 

4、SingleThreadExecutor:单线程化线程池

创建方法:

ExecutorService singleThreadExecutor= Executors.newSingleThreadExecutor()

特性:

①、有且仅有一个工作线程执行任务;

②、所有任务按照指定顺序执行,遵循FIFO原则。

 

5、SingleThreadScheduleExecutor:只有一个线程,用来调度执行任务。

创建方法:

ExecutorService singleThreadScheduleExecutor=Executors.newSingleThreadScheduleExecutor();

 

五、ExecutorService执行

1、execute(Runnable)接受一个Runnable实例,并且异步的执行,没有办法获知task的执行结构。

2、submit(Runnable)返回一个Future对象,通过返回的Future对象,我们可以检查任务是否执行完毕(future.get())。

3、submit(Callable)  返回一个Future对象,但除此之外,还接受Callable,Callable接口中call方法有一个返回值,可以返回任务执行结果,而Runnable接口中run()方法返回void,没有返回值

4、invokeAny(...)接收的是一个Callable的集合,执行这个方法不会返回Future,但是会返回所有Callable任务中其中一个任务执行结果。该方法不能保证返回是那个执行结果。

5、anvokeAll(...)接收一个Callable集合,同时返回Future的List,其中对应着每个Callable任务执行后的Futrue对象。

 

六、ExecutorService关闭

1、ExecutorService.shutdown():关闭执行任务对象。 在调用shutdown方法之后,ExecutorService不会立即关闭,但是不会接收新的任务,直到当前所有线程任务执行完成才会关闭。

2、ExecutorService.shutdownNow():立即关闭ExecutorService。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值