重点归纳-多线程

线程-java内存模型(共享内存模型)

定义
1、在并发编程中,需要处理两个关键问题:线程之间如何通信及同步;而Java线程之间的通信由Java内存模型控制,Java内存模型决定一个线程对共享变量的写入何时对另一个线程可见(但是会存在内存可见性问题,需要通过显式的同步机制去处理线程间的执行顺序(相对)问题)
2、Java内存模型将所有的共享变量都存储在主内存中(虚拟机内存的一部分),每个线程还有自己的工作内存
3、线程的工作内存中保存了被该线程使用的共享变量的主内存副本( 不是整个对象,而是对象中具体的变量),线程对共享变量的所有读写操作都必须在工作内存中进行,而不能直接读写主内存中的数据
4、不同的线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成(线程A把本地内存中更新过的共享变量刷新到主内存中去,线程B到主内存中去读取线程A之前已更新过的共享变量)
顺序一致性内存模型(理论模型)
特性:
1、一个线程中的所有操作必须按照程序的顺序来执行
2、无论程序是否同步,所有线程都只能看到一个单一的操作执行顺序;在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见
JMM特性:
1、未同步程序在JMM中不但整体的执行顺序是无序的,而且所有线程看到的操作执行顺序也可能不一致
2、当前线程把数据缓存在本地内存中,在没有刷新到主内存之前,这个写操作仅对当前线程可见;只有当前线程把本地内存中写过的数据刷新到主内存之后,这个写操作才能对其他线程可见;因此,当前线程和其他线程看到的操作执行顺序将不一致
3、临界区内的代码可以重排序(但JMM不允许临界区内的代码“逸出”到临界区之外,那样会破坏监视器的语义),JMM会在退出临界区和进入临界区这两个关键时间点做一些特别处理,虽然线程在临界区内做了重排序,但由于监视器互斥执行的特性,其他线程看不到当前线程在临界区内的重排序
4、JMM不保证单线程内的操作会按程序的顺序执行;JMM不保证所有线程能看到一致的操作执行顺序

原子性、可见性、有序性
Java内存模型需要处理3个问题:原子性、可见性和有序性
原子性:
1、由JMM来直接保证的原子性变量操作包括read、load、assign、use、store和write这六个, 基本数据类型的访问、读写都是具备原子性的(例外就是long和double的非原子性协定)
2、如果应用场景需要一个更大范围的原子性保证(经常会遇到),JMM还提供了lock和unlock操作来满足这种需求,尽管虚拟机未把lock和unlock操作直接开放给用户使用,但是却提供了更高层次的字节码指令monitorenter和monitorexit来隐式地使用这两个操作;这两个字节码指令反映到Java 代码中就是同步块——synchronized关键字,因此在synchronized块之间的操作也具备原子性
可见性:
1、可见性就是指当一个线程修改了共享变量的值时,其他线程能够立即得知这个修改---JMM是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存通信的方式来实现可见性的,无论是普通变量还是volatile变量都是如此
2、普通变量与volatile变量的区别是,volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新;因此我们可以说volatile保证了多线程操作 时变量的可见性,而普通变量则不能保证这一点
3、同步块的可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)”这条规则获得的
4、final关键字的可见性是指:被final修饰的字段在构造器中一旦被初始化完成,并且构造器没有把“this”的引用传递出去(this引用逃逸是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那么在其他线程中就能看见final字段的值
有序性:
1、线程内所有的操作都是有序的(串行语义);线程间所有的操作是无序的(指令重排序、工作内存与主内存同步延迟)
2、Java语言提供了volatile和synchronized两个关键字来保证线程之间操作的有序性,---volatile关键字本身就包含了禁止指令重排序的语义
3、synchronized则是由“一个变量在同一个时刻只允许一条线程对其进行lock操作”这条规则获得的,这个规则决定了持有同一个锁的两个同步块只能串行地进入
先行发生原则-happens-before
定义:在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在先行发生关系,先行发生原则用于描述内存可见性;两个操作既可以是在一个线程之内,也可以是在不同线程之间
两个操作之间具有先行发生关系,并不意味着前一个操作必须要在后一个操作之前执行,先行发生仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前
JMM先行发生(不是时间上的先后)规则:
1、程序顺序规则:一个线程中的每个操作,先行发生于该线程中的任意后续操作
2、监视器锁规则:对一个锁的解锁,先行发生(时间上的先后)于随后对这个锁的加锁
3、volatile变量规则:对一个volatile变量的写,先行发生(时间上的先后)于任意后续对这个volatile变量的 读
4、Thread对象的start方法先行发生于此线程的每一个动作
5、线程中的所有操作都先行发生于对此线程的终止检测
6、对线程interrupt方法的调用先行发生于被中断线程的代码检测到中断事件的发生
7、一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize方法的开始
8、传递性:如果A先行发生于B,且B先行发生于C,那么A先行发生于C

volatile(可见性、有序性)

volatile读:如果主内存变量值有更新,将本地内存中变量值失效,直接从主内存中读取最新值
volatile写:更新本地内存的同时,会立即刷新到主内存(主内存更新时会通知其他线程)

synchronized(可见性、有序性、原子性,互斥可重入,锁是存放在对象头里面的)

同步块:monitorenter和monitorexit指令实现
同步方法:依靠方法修饰符上的ACC_SYNCHRONIZEN同步位来实现
如果线程获取锁失败,线程进入同步队列,线程状态变为BLOCKED
当获取锁的线程释放了锁,会同时唤醒阻塞在同步队列中的线程,使其重新尝试对锁的获取
等待通知机制:
wait方法:前提是已经获取了锁,调用该方法的线程从运行状态变成等待状态(线程进入等待队列)并释放锁,只有等待其他线程的通知或中断,超时才会返回
notify方法:前提是已经获取了锁,将等待的线程从等待队列移到同步队列(等待状态变成阻塞状态),但当前线程可能还未执行完并释放锁,等待的线程不会从wait方法返回(获取锁才会返回)


image.png

锁升级(不能降级):
1、无锁
2、偏向锁:Mark Word中存偏向线程id、对象分代年龄、是否是偏向锁标志、锁标志位;单线程时,避免了CAS加锁/解锁,只需判断偏向线程id是否是自己,如果不是,且现在是偏向锁,则CAS将偏向线程id替换为自己,否则,CAS竞争锁,到全局安全点,暂停偏向线程,释放锁,锁变成无锁状态(偏向线程已经执行完逻辑)或轻量级锁
3、轻量级锁:Mark Word中存放指向持有锁线程的栈中锁记录的指针、锁标志位;CAS替换锁记录指针,失败,则自旋(避免上下文切换)+CAS获取锁,超过一定时间(次数)膨胀为重量级锁;追求响应时间
4、重量级锁:Mark Word中存指向获取锁的线程的栈中锁记录的指针、锁标志位;线程阻塞,不使用自旋,不消耗CPU;追求吞吐量

final(可见性、有序性、原子性)

final写:在构造方法内的final写与被构造对象的引用赋值,禁止重排序(即禁止将final写重排序到构造方法外)
final读:初次读对象引用与初次读对象的final常量,禁止重排序
注:特例,被构造对象在构造方法中通过引用赋值逃逸(构造方法中对象引用赋值和final写可能会重排序)

原因:如果不这样做,就可能导致多个线程看到的final值不一致(默认初始化值和初始值)

JUC-AQS

1、AQS支持独占锁(如ReentrantLock、ReadWriteLock的写锁)和共享锁(如CountDownLatch、ReadWriteLock的读锁)
2、state:表示资源数,volatile修饰,需要保证可见性、有序性、利用CAS保证原子性操作
3、队列节点等待状态:初始状态、取消状态(线程超时或被中断时,会进入取消状态,取消状态不会再往下执行)、唤醒状态(表示当前节点的后继节点正在等待获取资源,当前节点在release或cancel时需要执行unpark来唤醒后继节点)、条件状态(当前节点为条件队列节点,这个状态在同步队列里不会被用到)、传播状态(针对共享锁,设置在head节点releaseShared(释放共享锁)操作需要被传递到下一个节点,用来保证后继节点可以获取共享资源)
4、nextWaiter属性:连接下一个等待condition的节点,或者在共享模式下作为一个特殊节点保存,用来判断是否为共享模式
5、同步队列:双向链表,队尾插入时会有线程竞争(自旋+CAS插入,需要前驱有效节点唤醒,使用LockSupport.park/unpark阻塞释放线程);队首表示获取资源的线程节点
**acquire方法:调用子类的tryAcquire方法尝试CAS获取资源,成功直接返回;失败则阻塞线程,创建节点,自旋+CAS将节点放入同步队列尾部,当前驱有效节点是头节点时,自旋+CAS尝试获取资源(前驱有效节点不为头节点时,阻塞当前线程,直到被前驱节点唤醒;即使当前线程中间被中断过,也可以自旋+CAS尝试获取资源);如果自旋+CAS尝试获取资源出现异常,要将当前节点状态置为取消
**release方法:调用子类的tryRelease方法尝试自旋+CAS释放资源,唤醒同步队列中的后继节点(中间会移除取消状态的节点),后继节点自旋+CAS尝试获取资源,获取成功,则从acquire方法返回
**acquireShared方法:调用子类的tryAcquireShared方法尝试CAS获取资源,成功直接返回;失败则阻塞线程,创建节点,自旋+CAS将节点放入同步队列尾部,当前驱有效节点是头节点时,自旋+CAS尝试获取资源(前驱有效节点不为头节点时,阻塞当前线程,直到被前驱节点唤醒;即使当前线程中间被中断过,也可以自旋+CAS尝试获取资源),获取资源成功后,将当前节点设置为头节点,如果还有剩余资源,让后续节点也尝试获取资源;如果自旋+CAS尝试获取资源出现异常(超时),要将当前节点状态置为取消
**releaseShared方法:调用子类的tryReleaseShared方法尝试自旋+CAS释放资源,唤醒同步队列中的后继节点(中间会移除取消状态的节点),后继节点自旋+CAS尝试获取资源,获取成功,则从acquireShared方法返回
6、子类需要实现的方法:tryAcquire/tryRelease(独占)、tryAcquireShared/tryReleaseShared(共享)、isHeldExclusively(有用到Condition才需要实现)
7、条件队列:双向链表,只有在使用了Condition(AQS的内部类)才会存在条件队列,在使用Condition的方法之前需要先获取锁
**await方法:调用之前当前线程需要先获取资源;创建节点,自旋+CAS将节点放入条件队列尾部;调用release方法释放当前线程已经持有的资源,并移除同步队列节点,唤醒同步队列中的后继节点,如果释放资源出现异常(超时),要将当前节点状态置为取消;当当前线程没有加入到同步队列时,进行自旋,并阻塞当前线程,直到当前线程被唤醒(从条件队列移到同步队列)或期间被中断,并记录中断状态;在同步队列中自旋+CAS尝试获取资源;如果当前节点的nextWaiter不为空,说明节点在获取锁时由于异常或者被中断而被取消,此时需要移除等待队列中取消状态的节点;如果期间被中断过,抛出中断异常
**signal方法:自旋+CAS将条件队列中节点移除,并创建新节点,添加到同步队列尾部,并唤醒线程,在同步中自旋+CAS尝试获取资源,获取成功从await方法返回

JUC-ReentrantLock

lock是显式的获取锁,拥有锁获取与释放的可操作性、非阻塞获取锁、可中断的获取锁(获取到锁的线程能响应中断:当获取到锁的线程被中断时,锁也会被释放)以及超时获取锁(如果超时未获取锁,会返回)等多种synchronized关键字所不具备的同步特性;不能将获取lock锁的过程写在try块中,因为如果在获取锁时发生了异常(已经获取锁),异常抛出时,锁也会被释放;在finally块中释放锁
ReentrantLock是一个可重入的互斥锁,也被称为“独占锁”,“独占锁”在同一个时间点只能被一个线程持有,而“可重入锁”可以被单个线程多次获取;ReentrantLock又分为“公平锁”和“非公平锁”(默认),它们的区别体现在获取锁的机制上:在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”机制下,如果锁是可获取状态,不管自己是不是在队列的head节点都会去尝试获取锁
内部有一个Sync类继承自AQS,有两个子类FairSync和NonfairSync
1、lock方法:
非公平:调用NonfairSync中的lock方法;尝试CAS获取资源,成功,设置当前线程持有资源;失败,调用AQS的acquire方法获取资源;调用NonfairSync中的tryAcquire方法尝试CAS获取资源(不会判断当前节点前是否还有节点,与公平锁区别):如果没有线程获取资源,CAS尝试获取资源,设置当前线程持有资源,如果当前线程已经获取过资源(可重入),在原来基础上+1(不需要CAS,因为无竞争),成功直接返回;失败则阻塞线程,创建节点,自旋+CAS将节点放入同步队列尾部,当前驱有效节点是头节点时,自旋+CAS尝试获取资源(前驱有效节点不为头节点时,阻塞当前线程,直到被前驱节点唤醒;即使当前线程中间被中断过,也可以自旋+CAS尝试获取资源);如果自旋+CAS尝试获取资源出现异常,要将当前节点状态置为取消
公平:调用FairSync中的lock方法;调用AQS的acquire方法获取资源;调用FairSync中的tryAcquire方法尝试CAS获取资源(要判断当前节点前是否还有节点,与非公平锁区别):如果没有线程获取资源,CAS尝试获取资源,设置当前线程持有资源,如果当前线程已经获取过资源(可重入),在原来基础上+1(不需要CAS,因为无竞争),成功直接返回;失败则阻塞线程,创建节点,自旋+CAS将节点放入同步队列尾部,当前驱有效节点是头节点时,自旋+CAS尝试获取资源(前驱有效节点不为头节点时,阻塞当前线程,直到被前驱节点唤醒;即使当前线程中间被中断过,也可以自旋+CAS尝试获取资源);如果自旋+CAS尝试获取资源出现异常,要将当前节点状态置为取消
2、unLock方法:公平和非公平一致;调用AQS的release方法释放资源;调用Sync的tryRelease方法尝试释放资源:获取资源的次数必须要等于释放资源的次数,这样才算是真正释放了资源,才可以设置持有资源的线程为空,不需要CAS,因为无竞争;唤醒同步队列中的后继节点(中间会移除取消状态的节点),后继节点自旋+CAS尝试获取资源,获取成功,则从acquire方法返回
3、tryLock方法:调用Sync的nonfairTryAcquire方法尝试请求获取资源(不会判断当前节点前是否还有节点,非公平):如果没有线程获取资源,CAS尝试获取资源,设置当前线程持有资源,如果当前线程已经获取过资源(可重入),在原来基础上+1(不需要CAS,因为无竞争),成功直接返回;失败则阻塞线程,创建节点,自旋+CAS将节点放入同步队列尾部,当前驱有效节点是头节点时,自旋+CAS尝试获取资源(前驱有效节点不为头节点时,阻塞当前线程,直到被前驱节点唤醒;即使当前线程中间被中断过,也可以自旋+CAS尝试获取资源);如果自旋+CAS尝试获取资源出现异常,要将当前节点状态置为取消
4、等待通知机制:使用AQS的Condition实现

JUC-CountDownLatch

CountDownLatch是一个同步辅助类,是通过AQS实现的一个可重入的共享锁,可响应中断,会直接抛出中断异常;在其他线程完成操作之前,可以有一个或多个线程等待;内部有一个Sync类,继承自AQS,实现了tryAcquireShared和tryReleaseShared方法
创建CountDownLatch时会指定AQS中state大小
1、await方法:调用AQS的acquireSharedInterruptibly方法获取共享资源;回调子类Sync的tryAcquireShared方法尝试获取资源(当state=0的时候才可以获取资源),成功直接返回;失败则阻塞线程,创建节点,自旋+CAS将节点放入同步队列尾部,当前驱有效节点是头节点时,自旋+CAS尝试获取资源(前驱有效节点不为头节点时,阻塞当前线程,直到被前驱节点唤醒;即使当前线程中间被中断过,也可以自旋+CAS尝试获取资源),获取资源成功后,将当前节点设置为头节点,如果还有剩余资源,让后续节点也尝试获取资源;如果自旋+CAS尝试获取资源出现异常(超时),要将当前节点状态置为取消;如果中间被中断,直接抛出中断异常
2、countDown方法:调用AQS的releaseShared方法释放共享资源;回调子类Sync的tryReleaseShared方法尝试自旋+CAS释放资源,唤醒同步队列中的后继节点(中间会移除取消状态的节点),后继节点自旋+CAS尝试获取资源,获取成功,则从acquireShared方法返回

JUC-ReentrantReadWriteLock

ReentrantReadWriteLock维护了一对相关的锁:共享锁readLock和独占锁writeLock
共享锁readLock用于读操作,能同时被多个线程获取;独占锁writeLock用于写入操作,只能被一个线程持有(支持锁降级:持有写锁的线程可以在写锁未释放之前获得读锁)
Condition只有在写锁中用到,读锁是不支持Condition的
内部有一个Sync类继承自AQS,有两个子类FairSync和NonfairSync:读锁和写锁共用一个状态,高16位标识读计数了,低16位标识写重入次数;内部有一个静态内部类继承自ThreadLocal用于读记录读线程重入次数
内部有两个静态内部类:ReadLock和WriteLock
1、ReadLock:内部有lock和unLock方法,参考CountDownLatch
**lock方法:调用AQS的acquireShared方法获取资源;回调Sync的tryAcquireShared方法尝试CAS获取资源(持有写锁的线程可以继续获取读锁,没有线程获取写锁,可以尝试CAS获取资源),成功,更新当前线程锁重入次数,直接返回;失败,则阻塞线程,创建节点,自旋+CAS将节点放入同步队列尾部,当前驱有效节点是头节点时,自旋+CAS尝试获取资源(前驱有效节点不为头节点时,阻塞当前线程,直到被前驱节点唤醒;即使当前线程中间被中断过,也可以自旋+CAS尝试获取资源),获取资源成功后,将当前节点设置为头节点,如果还有剩余资源,让后续节点也尝试获取资源;如果自旋+CAS尝试获取资源出现异常(超时),要将当前节点状态置为取消;如果中间被中断,直接抛出中断异常
**unLock方法:调用AQS的releaseShared方法释放共享资源;回调子类Sync的tryReleaseShared方法尝试自旋+CAS释放资源,更新线程重入次数;唤醒同步队列中的后继节点(中间会移除取消状态的节点),后继节点自旋+CAS尝试获取资源,获取成功,则从acquireShared方法返回
2、WriteLock:内部有lock和unLock方法,参考ReentrantLock

线程池

一般不手动创建线程,而是使用线程池(降低资源损耗(创建和销毁线程)、提高响应速度、便于管理)
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式(规避资源耗尽的风险)
原因:
Executors可以创建3种类型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadPool和CachedThreadPool
1、FixedThreadPool 和 SingleThreadPool:阻塞队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM
2、CachedThreadPool 和 ScheduledThreadPool:最大线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM
线程池调优:CPU密集型-CPU核数+1,IO密集型-CPU*2,使用有界阻塞队列

线程池原理

核心组件
1、corePool:核心线程池的大小
2、maximumPool:最大线程池的大小
3、BlockingQueue:用来暂时保存任务的工作队列(阻塞队列,为什么用阻塞队列)
4、RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池大小且工作队列已满),execute()方法将要调用的Handler
5、线程池工厂:可以通过工厂为线程设置名字
拒绝策略:
1、AbortPolicy:默认策略,在需要拒绝任务时抛出RejectedExecutionException
2、CallerRunsPolicy:直接在 execute 方法的调用线程中运行被拒绝的任务,如果线程池已经关闭,任务将被丢弃
3、DiscardPolicy:直接丢弃任务
4、DiscardOldestPolicy:丢弃队列中等待时间最长的任务,并执行当前提交的任务,如果线程池已经关闭,任务将被丢弃
5、也可以自定义拒绝策略
原理:(为什么要这样设计:避免获取全局锁,完成预热之后,几乎都是在执行第的二步)
1、向线程池提交任务:execute方法(没有返回值)和submit方法(有返回值future,通过future的get方法可以获取返回值(阻塞当前线程,直到任务完成,返回结果))
2、如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务,否则向下执行(需要获取全局锁)
3、如果工作队列未满,将任务加入工作队列,否则向下执行
4、如果当前运行的线程数少于maximumPoolSize,则创建新线程来执行任务,否则向下执行(需要获取全局锁)
5、执行拒绝策略
6、关闭线程池:shutdown(推荐)和shutdownNow方法,遍历线程池中的工作线程,逐个调用线程的interrupt方法来中断线程(无法响应中断的任务可能永远无法终止);shutdown中断的是所有没有正在执行任务的线程(还没停止),shutdownNow中断的是所有正在执行或暂停任务的线程(立即停止)
注:线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,会循环从工作队列里获取任务进行执行

Future接口与FutureTask实现类

FutureTask既实现了Future接口,又实现了Runnable接口
FutureTask基于AQS实现
get方法:阻塞当前线程(同步队列中)直到获取结果(类似于acquire方法)
run和cancel方法:改变AQS的状态,唤醒阻塞线程

Executors

FixedThreadPool:可重用固定线程数的线程池,适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,即负载比较重的服务器
1、FixedThreadPool的corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads
2、当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止;这里把keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止
3、FixedThreadPool使用无界队列LinkedBlockingQueue作为线程池的工作队列(队列的容量为Integer.MAX_VALUE)(不会拒绝任务,可能会堆积大量的请求,从而导致 OOM)
4、当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中的线程数不会超过corePoolSize
5、使用无界队列时maximumPoolSize和keepAliveTime将是效参数

SingleThreadExecutor:单个线程(如果内部工作线程由于异常而被终止,则会新建一个线程替代)的线程池,适用于需要保证顺序地执行各个任务,并且在任意时间点,不会有多个线程是活动的应用场景
1、SingleThreadExecutor的corePoolSize和maximumPoolSize被设置为1
2、其他参数与FixedThreadPool相同
3、SingleThreadExecutor使用无界队列LinkedBlockingQueue作为线程池的工作队列(队列的容量为Integer.MAX_VALUE)

CachedThreadPool:大小无界的线程池,适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器
1、CachedThreadPool的corePoolSize被设置为0,即corePool为空;maximumPoolSize被设置为Integer.MAX_VALUE,即maximumPool是无界的
2、keepAliveTime设置为60L,意味着CachedThreadPool中的空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被终止
3、CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,但CachedThreadPool的maximumPool是无界的;这意味着,如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新线程;极端情况下, CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源
5、SynchronousQueue是一个没有容量的阻塞队列,每个插入操作必须等待另一个线程的对应移除操作,反之亦然
6、CachedThreadPool使用SynchronousQueue,把主线程提交的任务传递给空闲线程执行
执行流程:
1、执行SynchronousQueue.offer(Runnable task);如果当前maximumPool中有空闲线程正在执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行 offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute()方法执行完成;否则向下执行
2、当初始maximumPool为空,或者maximumPool中当前没有空闲线程时,将没有线程执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS);这种情况下,1将失败,此时CachedThreadPool会创建一个新线程执行任务,execute()方法执行完成
3、2中新创建的线程将任务执行完后,会执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS);这个poll操作会让空闲线程最多在SynchronousQueue中等待60秒钟;如果60秒钟内主线程提交了一个新任务(主线程执行1)),那么这个空闲线程将执行主线程提交的新任务;否则,这个空闲线程将终止;由于空闲60秒的空闲线程会被终止,因此长时间保持空闲的CachedThreadPool不会使用任何资源

你可能感兴趣的:(重点归纳-多线程)