目录
一、Java构建线程的方式
1、继承Thread
2、实现Runable
3、实现Callable
4、 线程池方式(java提供了构建线程池的方式)
java提供了Executors可以去创建(在阿里规范中不能使用这种方式,因为这种方式对线程的控制力度比较低)
手动创建线程池
二、线程池基本参数
public ThreadPoolExecutor(int corePoolSize,\\核心线程数量
int maximumPoolSize,\\最大线程数量
long keepAliveTime,\\最大空闲时间
TimeUnit unit,\\时间单位
BlockingQueue<Runnable> workQueue,\\ 任务队列
ThreadFactory threadFactory,\\ 线程工厂
RejectedExecutionHandler handler)\\ 拒绝策略,饱和处理机制)
{…………}
1、任务队列(阻塞队列):
当线程还有未执行完时,又要添加新的线程时就要把线程放入阻塞队列
线程池 | 阻塞队列 |
FixedThreadPool | LinkedBlockingQueue |
SingleThreadExecutor | LinkedBlockingQueue |
CachedThreadPool | SynchronousQueue |
ScheduledThreadPool | DelayedWorkQueue |
SingleThreadScheduledExecutor | DelayedWorkQueue |
1)ArrayBlockingQueue:
规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的.
1:它是有界阻塞队列。它是数组实现的,是一个典型的“有界缓存区”。数组大小在构造函数指定,而且从此以后不可改变。
2:是它线程安全的,是阻塞的,具体参考BlockingQueue的“注意4”。
3:不接受 null 元素
4:公平性 (fairness)可以在构造函数中指定。如果为true,则按照FIFO 顺序访问插入或移除时受阻塞线程的队列;如果为 false,则访问顺序是不确定的。
5:它实现了BlockingQueue接口。关于BlockingQueue,请参照《BlockingQueue》
6:此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选 方法。
7:其容量在构造函数中指定。容量不可以自动扩展,也没提供手动扩展的接口。
8:在JDK5/6中,LinkedBlockingQueue和ArrayBlocingQueue等对象的poll(long timeout, TimeUnit unit)存在内存泄露Leak的对象是AbstractQueuedSynchronizer.Node,
2)LinkedBlockingQueue:
大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的并发库中的BlockingQueue是一个比较好玩的类,顾名思义,就是阻塞队列。该类主要提供了两个方法put()和take(),前者将一个对象放到队列中,如果队列已经满了,就等待直到有空闲节点;后者从head取一个对象,如果没有对象,就等待直到有可取的对象。
3)PriorityBlockingQueue:
类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序.
1:它是无界阻塞队列,容量是无限的,它使用与类PriorityQueue相同的顺序规则。
2:它是线程安全的,是阻塞的
3:不允许使用 null 元素。
4:对于put(E o)和offer(E o, long timeout, TimeUnit unit),由于该队列是无界的,所以此方法永远不会阻塞。因此参数timeout和unit没意义,会被忽略掉。
5:iterator() 方法中所提供的迭代器并不保证以特定的顺序遍历 PriorityBlockingQueue 的元素。
如果需要有序地遍历,则应考虑使用 Arrays.sort(pq.toArray())。
6.至于使用和别的BlockingQueue(ArrayBlockingQueue,LinkedBlockingQueue)相似,可以参照它们。
7:此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选 方法。
4)SynchronousQueue:
特殊的BlockingQueue,对其的操作必须是放和取交替完成的.(每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,)此队列不允许 null 元素。
SynchronousQueue的定义如下
public classSynchronousQueueextendsAbstractQueueimplementsBlockingQueue,Serializable
从上面可以看出,它实现BlockingQueue,所以是阻塞队列,从名字看,它又是同步的。
它模拟的功能类似于生活中一手交钱一手交货这种情形,像那种货到付款或者先付款后发货模型不适合使用SynchronousQueue。
首先要知道SynchronousQueue没有容纳元素的能力,即它的isEmpty()方法总是返回true
另外在创建SynchronousQueue时可以传递一个boolean参数来指定它是否是访问它的线程按遵守FIFO顺序处理,true表示遵守FIFO。
5)DelayQueue
是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。
2、线程工厂:
该接口的目的是定制一个线程,可以设置线程的优先级、名字、是否后台线程、状态等。
其实就是提高线程的管理性,当线程发生问题时可以准确定位
3、拒绝策略:
-
AbortPolicy
该策略是线程池的默认策略。使用该策略时,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
-
DiscardPolicy
这个策略和AbortPolicy的slient版本,如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常。
-
DiscardOldestPolicy
这个策略从字面上也很好理解,丢弃最老的。也就是说如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列。
因为队列是队尾进,队头出,所以队头元素是最老的,因此每次都是移除对头元素后再尝试入队
-
CallerRunsPolicy
使用此策略,如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行。就像是个急脾气的人,我等不到别人来做这件事就干脆自己干。
-
自定义
如果以上策略都不符合业务场景,那么可以自己定义一个拒绝策略,只要实现RejectedExecutionHandler接口,并且实现rejectedExecution方法就可以了。具体的逻辑就在rejectedExecution方法里去定义就OK了。
三、常见线程池
①newSingleThreadExecutor
单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务
②newFixedThreadExecutor(n)
固定数量的线程池,每提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行
③newCacheThreadExecutor(推荐使用)
可缓存线程池, 当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,又智能的添加新线程来执行。
④newScheduleThreadExecutor
大小无限制的线程池,支持定时和周期性的执行线程
四、线程池执行流程
1、首先提交一个新的任务到线城池
2、检测是否有空闲的核心线程,如果有就就执行,没有的话就放入阻塞队列
3、检测阻塞队列是否满了,没满放入队列,满了进入下一部
4、加测是否到达最大线程数,到了就执行对应的拒绝策略,没到就创建非核心线程
五、线程池内的标识
//是一个int类型的数值,表达了两个意思1:声明当前线程池的状态, 2:声明线程池中的线程数
//高3位:线程池状态, 低29位是:线程池中的线程个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;//29位,方便做位运算
private static final int CAPACITY = (1 << COUNT_BITS) - 1;//通过位运算获得最大容量
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;//将-1的补码左移29位,也就是前三位是111,代表正常接受任务
private static final int SHUTDOWN = 0 << COUNT_BITS;//000 代表不接受新任务,但是仍然执行正在执行的任务,还有阻塞队列内的任务
private static final int STOP = 1 << COUNT_BITS;//001 代表不接收新任务,内部的任务也不处理,现在正在执行的任务也终止
private static final int TIDYING = 2 << COUNT_BITS;//010 代表线程池为过渡状态,即将结束
private static final int TERMINATED = 3 << COUNT_BITS;//011 线程池已经结束,执行terminated()方法才会到达此状态
private static int runStateOf(int c) { return c & ~CAPACITY; }//得到线程池的状态
private static int workerCountOf(int c) { return c & CAPACITY; }//得到线程池的线程数量
过程转换
六、源码解析
public void execute(Runnable command) {
if (command == null)//判断NULL
throw new NullPointerException();
int c = ctl.get();//拿到之前我们所说的32的int
if (workerCountOf(c) < corePoolSize) {//获取 工作线程数 < 核心线程数
//如果小于那么创建核心线
if (addWorker(command, true))
return;//如果创建成功就直接返回,结束
//如果发生失败,重新获取int值。失败是因为没有任何锁机制,多线程并发导致错误。
c = ctl.get();
}
//判断线程池是否是Running状态,
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))//再次检测状态,如果不是running状态就移除任务
reject(command);//执行拒绝策略
else if (workerCountOf(recheck) == 0)//如果是Running状态,但是工作线程数是0
addWorker(null, false);//那么就添加一个执行null任务的线程,用来处理阻塞队列中的任务
}
else if (!addWorker(command, false))//这时要是没有放进阻塞队列,证明队列已经满了,添加非核心线程,如果再次失败,就执行拒绝策略。
reject(command);
}
接下是Work源码
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);//继续获取线程池状态
if (rs >= SHUTDOWN &&//大于SHUTDOW状态就是指 ,running以外的状态
! (rs == SHUTDOWN &&//rs等于==shutdown
firstTask == null &&//任务为null,并且线程池转态不是Running,不需要处理
! workQueue.isEmpty()))//如果阻塞队列为null,返回false,外侧取反是ture
//构建线程失败
return false;
for (;;) {
int wc = workerCountOf(c);//获取工作线程数
if (wc >= CAPACITY ||//当前线程数,大于线程线程池最大的容量,不去创建
wc >= (core ? corePoolSize : maximumPoolSize))//判断核心线程或者最大线程
return false;
//将工作线程数+1,采用CAS的方式
if (compareAndIncrementWorkerCount(c))
//成功就退出外侧循环
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
//检测高并发,重新判断现在的状态,如果不符合就结束这次循环,开启下次外侧循环
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);//创建worker
final Thread t = w.thread;//从work获取线程
if (t != null) {
//获取线程池的全局锁,避免我添加任务时,有其他线程shutdown当前的线程池,所以需要先获得这个锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();//再次获得线程锁
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN || //如果是running状态
(rs == SHUTDOWN && firstTask == null)) {//如果现在是shutdown状态,创建null任务工作线程,处理阻塞队列任务
if (t.isAlive()) // 线程是否是工作状态
throw new IllegalThreadStateException();
workers.add(w);//将工作线程数添加到hashset集合中
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;//如果线程工作数量,大于之前之前记录的就更新一下
workerAdded = true;//添加成功
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();//启动工作线程
workerStarted = true;启动成功
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);//如果启动失败
}
return workerStarted;//返回工作状态
}
worker的封装
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {//work有参构造方法
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);//通过线程工厂把自己传过来
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);//主要就是看这个runWorker方法
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
runWork方法
final void runWorker(Worker w) {
//获取当前线程
Thread wt = Thread.currentThread();
//拿到任务
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;//标识为true
try {
//任务不空,执行任务。 如果任务为null,通过getTask获取任务,不停获取,通过await和signal唤醒
while (task != null || (task = getTask()) != null) {
w.lock();//加锁,避免其他线程shutdown,导致我任务停止
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||//判断当前线程是否大于等于STOP,直接被拒
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();//中断
try {
//执行任务前操作,类似与AOP
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();//任务执行
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
//执行任务后操作
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
后期你也要看getTask