ThreadPool和ThreadLocal

ThreadPool是什么?

ThreadPool是指线程池!提到线程池大家肯定会想到Callable吗?线程的第三种实现,Callable接口与Runnable不同之处在于Callable有返回值.线程池相当于一个容器,具体的实现包括如下:


一般通过Executor框架的工具类Executors来创建ThreadPoolExecutor,可以创建三种类型的ThreadPoolExecutor

1.  FixedThreadPool

2.  SingleThreadPool

3.  CachedThreadPool


FixedThreadPool

FixedThreadPool被称为可重用固定线程数的线程池。源代码:

public static ExecutorServicenewFixedThreadPool(int nThreads) {

        return newThreadPoolExecutor(nThreads, nThreads,

                                      0L,TimeUnit.MILLISECONDS,

                                      newLinkedBlockingQueue<Runnable>());

}

nThreads:FixedThreadPool指定参数

当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后的多余的线程将被终止。这里把keepAliveTime设置为0L,意味多余的线程将会被立即终止。

FixedThreadPool的execute()方法的运行示意图如图


说明:

1.   如果当前运行的线程数小于corePoolSize,则创建新线程。

2.   在完成预热后,将任务加入LinkedBlockingQueue。

3.   线程执行完1中的任务后,将会在循环中反复从LinkedBlockingQueue获取任务来执行。

FixedThreadPool使用无界队列LinkedBlockingQueue作为线程池的工作队列(容量为Integer.MAX_VALUE)。使用无界队列作为工作队列会对线程池带来如下影响。

1.   当线程池中的线程数达到corePoolSize后,新任务将在LinkedBlockingQueue中等待,因此线程池中线程数不会超过corePoolSize。

2.   由于1,使用无界队列时maximumPoolSize将是一个无效参数。

3.   由于1和2,使用无界队列时keepAliveTime将是一个无效参数。

4.   由于使用无界队列,运行中的FixedThreadPool(未执行方法shutdown()或shoutdownNow())不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution()方法)。

 

SingleThreadPool

SingleThreadPool是使用单个worker线程的Executor。源代码实现:

public static ExecutorServicenewSingleThreadExecutor() {

        return newFinalizableDelegatedExecutorService

            (new ThreadPoolExecutor(1,1,

                                    0L,TimeUnit.MILLISECONDS,

                                    newLinkedBlockingQueue<Runnable>()));

}

和FixedThreadPool基本一样,只是将初始值设置为1。

CachedThreadPool

CachedThreadPool是一个会根据需要来创建新线程的线程池。源代码实现:

public staticExecutorService newCachedThreadPool() {

       return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

                                      60L, TimeUnit.SECONDS,

                                      newSynchronousQueue<Runnable>());

}

corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE,即maximumPool是无界的,keepAliveTime为60L,线程最大时间为60秒。

因为maximumPoolSize为无界的,如果主线程提交任务的速度高于CachedThreadPool中线程处理任务的速度,CachedThreadPool会不断创建线程,极端情况下,CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源。

CachedThreadPool的execute()方法的运行示意图如图:


说明:

1.  首先执行SynchronousQueue.offer(Runnabletask)。如果当前maximumPoolSize中有空闲线程正在执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行offer操作与空闲线程执行的poll操作配对成功,主线程吧任务交给空闲线程执行,execute()方法执行完成;否则执行下面的步骤2。

2.  当初始maximumPool为空,或者maximumPool中没有空闲线程时,将没有线程执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)操作,这种情况下步骤1将失败,此时CachedThreadPool将会创建一个新线程来执行任务,execute()方法执行完成。

3.  在步骤2中创建新的线程将任务执行完成后,会执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)操作,这个poll操作将会让线程最多在SynchronousQueue中等待60秒。如果60秒钟内主线程提交了一个新任务(主线程执行步骤1),那么这个空闲线程将执行主线程提交的新任务;否则这个空闲线程将终止,因此长时间空闲的CachedThreadPool不会使用任何资源。

ScheduledThreadPoolExecutor

主要用于在给定的延迟之后执行任务,或者定期执行任务,比Timer更强大,更灵活,基于DealyQueue。

执行主要分为两大部分

1) scheduleAtFixedRate()方法或者scheduleWithFixedDelay()会向ScheduledThreadPoolExecutor中添加一个实现了RunnableScheduledFuture接口的ScheduledFutureTask

2) 线程池中的线程从DealyQueue中能够获取ScheduledFutureTask,然后执行任务。

具体实现

ScheduledFutureTask主要包含3个成员变量

1)   long time,表示这个恩物将要被执行的具体时间。

2)   long sequenceNumber,表示这个任务被添加到ScheduledThreadPoolExecutor中的序号。

3)   long period,表示任务执行间隔周期。

DealyQueue封装了一个priorityQueue,这个priorityQueue会对ScheduledFutureTask进行排序。排序时,time小的排在前面。如果time相同,则比较sequenceNumber,小的排在前面,则先加入的先执行。

如图:


1)   线程1从队列中获取到已到期的任务,是指任务的时间于等于当前时间。

2)   线程1执行任务。

3)   线程1修改任务的time变量为下次将要执行的时间。

4)   线程1把修改time之后任务重新放回队列。

步骤1获取任务过程图:


1.   获取lock。

2.   获取周期任务

2.1 如果priorityQueue为空,当前线程到Condition中等待;否则执行下面的2.2;

如果priorityQueue的头元素的time时间比当前时间大,到Condition中等待到2.2 time时间;否则执行下面的2.3;

2.3  获取priorityQueue的头元素(2.3.1);如果priorityQueue不为空,则唤醒在Condition中等待的所有线程(2.3.2)。

3.   释放lock。

ScheduledThreadPoolExecutor在一个循环中执行步骤2,直到线程从priorityQueue获取到一个元素后,才会退出无线循环。

 

步骤4添加任务过程图


1.   获取lock。

2.   添加任务。

2.1 向priorityQueue添加任务。

2.2 如果在上面2.1中添加的任务是priorityQueue的头元素,唤醒在Condition中等待的所有线程。

3.   释放lock。

ThreadLocal是什么?

ThreadLoacl相当于一个容器,存放本地线程变量,意思是说在多线程环境下,线程自己操作的变量不需要和其他线程进行交互,name就可以将这类变量存放在本地线程中,还有一个点不知道大家能注意到吗?线程是在栈上开的内存,本地线程的内存也在线程中,这意味着线程执行完毕,本地线程内的数据就消失了,可以有效地降低JVM的垃圾回收时间,当然最重要的一点是拿来做多线程下数据安全的保障.

总结:线程池部分借鉴了我大学同学也是我的好老师张亮给我的笔记,希望对大家有用,不足或错误的地方希望支出,Thanks♪(・ω・)ノ!

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值