计算机线程
线程是调度CPU的最小单元,也叫轻量级进程LWP(Light Weight Process)
线程模型分类:
-
用户级线程:用户程序实现,不依赖操作系统核心,应用提供创建、同步、调度和管理线程的函数来控制用户线程。不需要用户态与内核态的切换,速度快。内核对用户线程无感知,线程阻塞则进程阻塞
-
内核级线程:系统内核管理线程,内核保存线程的状态和上下文信息,线程阻塞不会引起进程阻塞。在多处理器系统上,多线程在多处理器上并行执行。线程的创建、调度和管理由内核完成,效率比用户线程要慢,比进程操作快
后面具体研究下,参考文献
https://www.cnblogs.com/still-smile/p/11656591.html
https://www.cnblogs.com/still-smile/p/11656533.html
1、线程池的意义
它的创建和销毁是比较重且消耗资源的操作,而Java线程依赖内核线程,创建线程需要进行操作系统状态切换,为了避免资源过去消耗需要设法重用线程执行多个任务。线程池就是一个线程缓存,负责对线程进行统一分配、调优与监控。
线程池的优势
1、重用存在的线程,减少线程创建,消亡的开销,提高性能
2、提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行
3、提高线程的可管理性,线程是稀缺资源,如果无限的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优与监控
2、几个重要接口和类的官方文档
Executor(接口)
Executor是一个对象,可以用来执行提交过来的Runnable任务,该接口提供了一种方式,将任务的提交和每一个任务具体的执行机制进行解耦了,包括线程使用细节,调度等等。通常使用Executor而不是显式创建线程,例如,比起使用这个创建线程 new Thread(new(RunnableTask())).start() ,你使用如下方式会更好
Executor executor = anExecutor;
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
...
ExecutorService(接口)
ExecutorService本质上是一个Executor,提供方法来管理终端和方法,该方法可以产生一个Future用来追踪一个或多个异步任务执行情况
一个ExecutorService
可以关闭,这将导致它拒绝新的任务。 提供了两种不同的方法来关闭ExecutorService
。 shutdown()
方法将允许先前提交的任务在终止之前执行,而shutdownNow()
方法可以防止等待任务启动并尝试停止当前正在执行的任务。 一旦终止,执行者没有任务正在执行,没有任务正在等待执行,并且不能提交新的任务。 应关闭未使用的ExecutorService
以允许资源的回收。
AbstractExecutorService(抽象类)
提供ExecutorService
中的接口执行方法的默认实现,此类使用包中提供的默认 FutureTask 类实现了 submit、invokeAny 和 invokeAll 方法
Executors(类)
一个工厂并且提供了一些辅助方法针对于Executor
中的接口ExecutorService
, ScheduledExecutorService
, ThreadFactory
和Callable
在此包中定义的类。该类支持以下几种方法:
- 创建并返回一个
ExecutorService
设置的常用的配置设置的方法。 - 创建并返回一个
ScheduledExecutorService
的方法, 其中设置了常用的配置设置。 - 创建并返回“包装”ExecutorService的方法,通过使实现特定的方法无法访问来禁用重新配置。
- 创建并返回将新创建的线程设置为已知状态的
ThreadFactory
的方法。 - 创建并返回一个方法
Callable
出的其他闭包形式,这样他们就可以在需要的执行方法使用Callable
。
作用:作为一个工厂并更加方便地创建出对应的线程池对象
3、使用工厂模式创建线程池
- newFixedThreadPool(int n):表示创建一个固定大小的线程池,corePoolSize = n,maximumPoolSize = n,即线程池当中所一直维护的线程数量 是 n,且最大的线程数量是n
- newSingleThreadExecutor():表示创建一个固定大小是1的线程池,corePoolSize = 1,maximumPoolSize = 1,即线程池当中所一直维护的线程数量 是 1,且最大的线程数量是1(线程池除了线程外,还有其他的记录功能,应该大小为1的线程池也是有好处)
- newCachedThreadPool():表示创建一个可缓存线程的线程池,corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE,即线程池当中所一直维护的线程数量 是 0,且最大的线程数量是Integer.MAX_VALUE。当线程超过特定时间无使用则自动回收,当线程不够时,则再次创建线程
总结:上面3种创建方式其实本质上都是对ThreadPoolExecutor进行传参从而构造线程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
注意:
①:一般中小项目才会使用工厂模式进行创建。而一般大项目会精心考量每一个参数的大小进行创建线程池,而不使用工厂模式,比较工长模式灵活性非常受限
②:线程池里面的线程都是用户线程,并非守护线程,因此线程池不用时,需要进行关闭,否则会浪费资源
例子
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class MyTest12 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
//ExecutorService executorService = Executors.newSingleThreadExecutor();
//ExecutorService executorService = Executors.newCachedThreadPool();
IntStream.range(0, 3).forEach(i -> {
executorService.submit(() -> {
IntStream.range(0, 5).forEach(j -> {
System.out.println(Thread.currentThread().getName());
});
});
});
executorService.shutdown();
}
}
使用newFixedThreadPool(3)输出,线程池中3个线程都能用上
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-3
pool-1-thread-3
pool-1-thread-3
pool-1-thread-3
pool-1-thread-3
使用newSingleThreadExecutor()输出,线程池中只有一个线程用上
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
使用newCachedThreadPool()输出,当线程不够时,会创建新的线程使用,线程数范围在[1, 3],因为例子中最多有3个任务,有可能运行得快只用上1个或者2个线程
pool-1-thread-1
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-3
pool-1-thread-3
pool-1-thread-3
pool-1-thread-3
pool-1-thread-3
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
4、线程池构造参数
- 1、int corePoolSize:线程池当中所一直维护的线程数量,如果线程池处于任务空闲期间,那么该线程也并不会被回收掉
- 2、int maximumPoolSize:线程池所维护的线程数量的最大数量
- 3、long keepAliveTime:超过了corePoolSize的线程在经过keepAliveTime时间后一直处于空闲状态,那么超过的这部分线程将会回收掉
- 4、TimeUnit unit:指的是keepAliveTime的时间单位
- 5、BlockingQueue<Runnable> workQueue:向线程池所提交的任务位于的阻塞队里,它的实现有多种方式
- 6、ThreadFactory threadFactory:线程工厂,用于创建新的线程并被线程池所管理,默认线程工厂所创建的线程都是用户线程且优先级为正常优先级
该接口最重要的方法
public interface ThreadFactory {
Thread newThread(Runnable r);
}
该接口有几个实现类,默认的实现类是DefaultThreadFactory,其中poolNumber要设置成static,threadNumber要设置成非static,因为线程池中的每个线程都是从1开始记录的
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
//当该线程是守护线程时,设置为用户线程
if (t.isDaemon())
t.setDaemon(false);
//设置线程的优先级相同
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
- 7、RejectedExecutionHandler handler:表示当线程池中的线程都在忙于执行任务且阻塞队里也已经满了的情况下,新到来的任务该如何被对待和处理。
它有四种实现策略
(1)AbortPolicy:直接抛出一个运行期异常
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
public class RejectedExecutionException extends RuntimeException {}
(2)DiscardPolicy:默默地丢弃掉提交的任务,什么都不做且不抛出异常
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
(3)DiscardOldestPolicy:丢弃掉阻塞队列存放时间最久的任务(队头元素),并且为当前所提交的任务留出一个队列中的空闲空间,以便将其放进队列中(当弹出队头时,可能又被其他任务入队了,这时会继续该操作,直到成功为止)
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
(4)CallerRunsPolicy:直接由提交任务的线程来运行这个提交的任务(虽然这么做不会丢失任务,可是本来主线程把该任务给线程池执行,自己可以做别的调度事情,用这个策略后最后还是自己执行,最终还是会阻塞主线程)
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
在线程池中,最好把偏向锁标记关闭,使用偏向锁会影响性能。(线程池是用来执行大量任务的集合体,大量的任务都是由不同的线程执行)
5、线程池拒绝策略实例分析
例子:初始线程池最小线程数是3,最大线程数是5,线程等待回收时间是0,使用有限长度的阻塞队列大小是3。一共有9个任务,正在执行的线程有5个,阻塞3个,表示当线程池中的线程都在忙于执行任务且阻塞队里也已经满了的情况下,
package com.concurrency2;
import java.util.concurrent.*;
import java.util.stream.IntStream;
public class MyTest12 {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(3, 5, 0,
TimeUnit.SECONDS, new LinkedBlockingQueue(3),
new ThreadPoolExecutor.AbortPolicy());
//DiscardPolicy()
IntStream.range(0, 9).forEach(i -> {
executorService.submit(() -> {
IntStream.range(0, 2).forEach(j -> {
System.out.println(Thread.currentThread().getName());
});
});
});
executorService.shutdown();
}
}
1、使用AbortPolicy()拒绝策略,根据该策略,当出现新到来的任务时,自动抛出运行时异常
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@4e50df2e rejected from java.util.concurrent.ThreadPoolExecutor@1d81eb93[Running, pool size = 5, active threads = 5, queued tasks = 3, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.concurrency2.MyTest12.lambda$main$2(MyTest12.java:12)
at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
at java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:557)
at com.concurrency2.MyTest12.main(MyTest12.java:11)
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-1
pool-1-thread-1
pool-1-thread-4
pool-1-thread-4
pool-1-thread-5
pool-1-thread-5
pool-1-thread-3
pool-1-thread-3
2、使用DiscardPolicy()拒绝策略,根据该策略,当出现新到来的任务时,直接扔掉,因此下面输出小于18个
pool-1-thread-3
pool-1-thread-3
pool-1-thread-3
pool-1-thread-3
pool-1-thread-1
pool-1-thread-1
pool-1-thread-3
pool-1-thread-3
pool-1-thread-2
pool-1-thread-2
pool-1-thread-1
pool-1-thread-1
pool-1-thread-4
pool-1-thread-4
pool-1-thread-5
pool-1-thread-5
3、使用DiscardOldestPolicy拒绝策略,根据该策略,当出现新到来的任务时,poll出阻塞队列的队头,将自己加进去,因为因此下面输出小于18
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-2
pool-1-thread-2
pool-1-thread-3
pool-1-thread-3
pool-1-thread-4
pool-1-thread-4
pool-1-thread-5
pool-1-thread-5
4、使用CallerRunsPolicy拒绝策略,根据该策略,当出现新到来的任务时,直接由提交任务的线程来运行这个提交的任务,因此线程数是18,其中有两个是由提交任务的线程执行,下面是main线程
main
main
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-2
pool-1-thread-2
pool-1-thread-3
pool-1-thread-3
pool-1-thread-4
pool-1-thread-4
pool-1-thread-5
pool-1-thread-5
6、关于线程池的总体执行策略:
image.png表述1
1、如果线程池中正在执行的线程数 < corePoolSize,那么线程池就会优先选择创建新的线程而非将提交的任务加到阻塞队列中
2、如果线程池中正在执行的线程数 >= corePoolSize,那么线程池就会优先进行阻塞队列排队而非创建新的线程
3、如果提交的任务无法加入到阻塞队列当中,那么线程池就会创建新的线程;如果创建的线程数超过了maximumPoolSize,那么拒绝策略就会起作用
表述2
1、当任务提交之后,线程池首先会检查当前线程数,如果当前的线程数小于核心线程数(corePoolSize),比如最开始创建的时候线程数为 0,则新建线程并执行任务。
2、当提交的任务不断增加,创建的线程数等于核心线程数(corePoolSize),新增的任务会被添加到 workQueue 任务队列中,等待核心线程执行完当前任务后,重新从 workQueue 中获取任务执行。
3、假设任务非常多,达到了 workQueue 的最大容量,但是当前线程数小于最大线程数(maximumPoolSize),线程池会在核心线程数(corePoolSize)的基础上继续创建线程来执行任务。
4、假设任务继续增加,线程池的线程数达到最大线程数(maximumPoolSize),如果任务继续增加,这个时候线程池就会采用拒绝策略来拒绝这些任务。
注意:当阻塞队列中已经没有任务了,同时该线程池中的线程也执行完任务了,它会进入到相应的Condition等待集合中进行阻塞,直到等到下一个任务提交时会直接进入到阻塞队列,便会唤醒Condition集合中的某一个线程。如果是非核心线程,它阻塞了一段时间后便会自动回收。
7、关于线程池任务提交的总结:
1、两种提交方式:submit与execute
2、submit有三种方式,无论哪种方式,最终都是将传递进来的任务转换为一个Callable对象进行处理。submit方法可以取代execute方法,因为它既可以接收Callable任务,也可以接收Runnable任务
3、当Callable对象构造完毕后,最终都会调用Executor接口中声明的execute方法进行统一的处理
submit的三种方法
方式一:接收Runnable对象,会转变为Callable对象,执行完后,返回null
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
方式二:接收Runnable对象,会转变为Callable对象,执行完后,返回指定的结果result
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
方式三:接收Callable对象,执行完后,返回任务处理后的结果
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
8、线程池的状态
对于线程池来说,存在两个状态需要维护:
1、线程池本身的状态:ctl 的高3位来表示
2、线程池中正在运行的线程的数量:ctl 的其余29位来表示
注意:其中 ctl 是AtomicInteger类型的
线程池一共存在5种状态
1、RUNNING:线程池可以接收新的任务提交,并且还可以正常处理阻塞队列中的任务
2、SHUTDOWN:不再接受新的任务提交,不过线程可以继续处理阻塞队列中的任务
3、STOP:不再接收新的任务,同时还会丢弃阻塞队列中的既有任务;此外,它还会中断正在处理中的任务
4、TIDYING:所有的任务都执行完毕后(同时也涵盖了阻塞队列中的任务),当前线程池中的活动的线程数量降为0,将会调用terminated方法
5、TERMINATED:线程池的终止状态,当terminated方法执行完毕后,线程池将会处于该状态之下
- RUNNING -> SHUTDOWN:当调用了线程池中的shutdown方法时,或者当finalize方法被隐式调用后(该方法内部会调用shutdown方法)
- RUNNING, SHUTDOWN -> STOP:当调用了线程池中的shutdownNow方法时,丢弃阻塞队列中的既有任务 和 中断正在处理中的任务,调用了shutdownNow方法会返回阻塞队列中的任务的一个新链表
- SHUTDOWN -> TIDYING:在线程池与阻塞队列均变为空时
- STOP -> TIDYING:在线程池变为空时
- TIDYING -> TERMINATED:在terminated方法被执行完毕后
9、execute()
execute方法,它会执行给定的任务在未来的某个时间。这个任务可能在新的线程当中执行,也可能在存在的线程池中执行。如果一个任务不能提交进行执行,有两种情况之一,①线程池已经关闭了 ,②阻塞队列容量已经满了,当满足其中一个,当前任务就会被拒绝策略处理
ThreadPoolExecutor#execute 源码分析
public void execute(Runnable command) {
// 如果传入的Runnable的空,就抛出异常
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 线程池中的线程比核心线程数少
if (workerCountOf(c) < corePoolSize) {
// 新建一个核心线程执行任务
if (addWorker(command, true))
return;
c = ctl.get();
}
// 核心线程已满,但是任务队列未满,添加到队列中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 任务成功添加到队列以后,再次检查是否需要添加新的线程,因为已存在的线程可能被销毁了
if (! isRunning(recheck) && remove(command))
// 如果线程池处于非运行状态,并且把当前的任务从任务队列中移除成功,则拒绝该任务
reject(command);
else if (workerCountOf(recheck) == 0)
// 如果之前的线程已经被销毁完,新建一个非核心线程
addWorker(null, false);
}
// 核心线程池已满,队列已满,尝试创建一个非核心新的线程
else if (!addWorker(command, false))
// 如果创建新线程失败,说明线程池关闭或者线程池满了,拒绝任务
reject(command);
}
其他代码比较容易看懂,这里的代码需要解释一下
else if (workerCountOf(recheck) == 0)
// 如果之前的线程已经被销毁完,新建一个非核心线程
addWorker(null, false);
进入这个 else 说明前面判断到线程池状态为 Running,那么当任务被添加进来之后就需要防止没有可执行线程的情况发生(比如之前的线程被回收了或意外终止了),所以此时如果检查当前线程数为 0,也就是 workerCountOf(recheck) == 0,那就执行 addWorker() 方法新建一个非核心线程。
addWorker源码分析
addWorker 方法的主要作用是在线程池中创建一个线程并执行传入的任务,如果返回 true 代表添加成功,如果返回 false 代表添加失败。
第一个参数firstTask表示新创建的线程首先执行的任务,也可以任务为null,
第二个参数是个布尔值,如果布尔值传入 true 代表增加线程时判断当前线程是否少于 corePoolSize,小于则增加新线程(核心线程),大于等于则不增加;同理,如果传入 false 代表增加线程时判断当前线程是否少于 maximumPoolSize,小于则增加新线程(非核心线程),大于等于则不增加,所以这里的布尔值的含义是以核心线程数为界限还是以最大线程数为界限进行是否新增线程
//线程启动的时候
private boolean addWorker(Runnable firstTask, boolean core) {
//代码 通过CAS操作对线程数量加1,
w = new Worker(firstTask);
final Thread t = w.thread;
...//代码
t.start();
...//代码
}
runWorker源码分析
线程池中的线程是封装在Worker类中的,启动线程时调用start执行run方法,执行runWorker,当第一个线程不为空 或者 阻塞队列中能够获取到线程(getTask()为true)时,线程会一直执行下去,即同一个线程不断会执行任务,从而达到了线程复用的效果
//1,线程启动的时候jvm会执行它
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
final Thread thread;
Runnable firstTask;
public void run() {
runWorker(this);
}
}
//2,继续看里面的runWorker(this);很显然this是内部类Work对象本身
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
../代码
try {
while (task != null || (task = getTask()) != null) {
try{
../代码
task.run();
}catch(){
../代码
} finally {
../代码
}
}
../代码
} finally {
../代码
}
}
当线程池中的线程已经没有任务可以执行了会怎么样???会自动回收吗?
答案:当阻塞队列中已经没有任务了,同时该线程池中的线程也执行完任务了,它会进入到相应的Condition等待集合中进行阻塞,直到等到下一个任务提交时会直接进入到阻塞队列,便会唤醒Condition集合中的某一个线程。如果是非核心线程,它阻塞了一段时间后便会自动回收
(1)线程进入Condition等待集合的源码
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
以阻塞队列是ArrayBlockQueue为例
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
分析:其实在getTask()方法的时候,如果该线程已经空闲了,就会调用workQueue.take()方法,会调用notEmpty.await()方法从而进入阻塞,其中notEmpty是Condition类型
(2)Condition等待集合的线程被唤醒
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
}
分析:当线程数量达到一定的核心线程数时,有任务提交时在execute方法中会调用workQueue.offer(command)加入任务,同时如果会唤醒阻塞中的核心线程
网友评论