1.线程的理解
用户线程(ULT):用户程序实现,不依赖操作系统核心,应用提供创建,同步,调度和管理线程的函数来控制用户线程。不需要用户态/核心态切换,速度快。内核对ULT无感知,线程阻塞则进程阻塞。
内核线程(KLT):系统内核管理线程,内核保存线程的状态和上下文信息,线程阻塞不会引起进程阻塞。在多处理器系统上,多线程再多处理器上并行运行。线程的创建,调度和管理由内核完成,效率比ULT要慢,比进程操作快。
2.Java线程与系统内核线程
Java线程创建是依赖于系统内核,通过JVM调用系统库创建内核线程,内核线程与Java-Thread是1:1的映射关系。
3.线程的执行原理解析
线程是稀缺资源,他的创建与销毁是一个相对偏重且消耗资源的操作,而java线程依赖于内核线程,创建线程需要进行操作系统状态切换,为了避免资源过度消耗需要设法重用线程执行多个任务。线程池就是一个线程缓存,负责对线程进行统一分配,调优与监控。
什么时候使用线程池?
单个任务处理时间比较短
需要处理的任务数量很大
线程池的优势
重用存在的线程,减少线程的创建,消亡的开销,提高性能
提高响应速度,当任务到达的时候,任务可以不需要等待线程创建就能立即执行。
提高线程的可管理性,可统一管理,调优与监控。
/**
* @author yhd
* @createtime 2020/9/5 10:26
*/
public class DemoG {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2,
3,
5,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
//new ThreadPoolExecutor.AbortPolicy() //抛异常
//new ThreadPoolExecutor.CallerRunsPolicy() //main 线程办理业务!
//new ThreadPoolExecutor.DiscardOldestPolicy() //就处理能处理的,剩下的老的直接丢了。
new ThreadPoolExecutor.DiscardPolicy() //如果新来的处理不了,直接就扔了。
);
for (int i = 0; i < 9; i++) {
pool.execute(new Lalala(i));
}
pool.shutdown();
}
}
class Lalala implements Runnable{
private int flag;
public Lalala(int flag){
this.flag=flag;
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程"+flag+"正在执行任务。。。。");
}
}
阻塞队列
FIFO
1.在任意时刻,不管并发有多高,永远只有一个线程能够进行队列的入队和出队操作!线程安全的队列。
2.队列分为有界队列和无界队列。
队列满,只能进行出队操作,所有入队的操作必须等待,也就是被阻塞。
队列空,只能进行入队操作,所有出队的操作必须等待,也就是被阻塞。
线程池7大核心参数
int corePoolSize
, 线程池中的常驻核心线程数
int maximumPoolSize
, 能够容纳同时执行的最大线程数,必须大于1
long keepAliveTime
, 多余空闲线程存活时间
TimeUnit unit
, 上个参数的单位
BlockingQueue<Runnable> workQueue
, 任务队列,被提交但是尚未执行的任务
ThreadFactory threadFactory
, 表示生成线程池中工作线程的线程工厂,用于创建线程,一般默认 RejectedExecutionHandler handler
,拒绝策略,表示当队列满了,并且工作线程大于等于线程池的最大连接数时,如何来拒绝请求执行的runnable的策略。
线程池和的执行流程
1、在创建了线程池后,线程池中的线程数为零。
2、当调用execute ()方法添加一个请求任务时,线程池会做出如下判断:
2.1如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;
2.2如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;
2.3如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
2.4如果队列满了且正在运行的线程数量大于或等于max imumPoolSize,那么线程池会启动饱和拒绝策略来执行。
3、 当一个线程完成任务时,它会从队列中取下一个任务来执行。
4、当一个线程无事可做超过一定的时间(keepAliveTime) 时,线程会判断:
如果当前运行的线程数大于corePoolSize.那么这个线程就被停掉。
所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。
排个坑,假如现在核心线程都在处理任务,等待队列也满了,再来任务肯定会用非核心线程执行,但是当他执行完的时候,他不会执行等待队列的任务,因为等待队列的任务都在等待着核心线程来执行。
线程池是如何实现复用的
老规矩,一言不合追源码~~~~
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
从源码可以看到,runWorker()方法里面的finally里面执行了processWorkerExit()方法,点进去看看这个方法:
这个方法里先判断线程池的状态,状态是running或者shutdown的时候,才满足条件,然后给他设置一个超时时间,判断如果当前工作线程数>核心线程数,直接就返回线程池。
4.java.util.concurrent.Executors
这是JUC提供的一个工具类帮助我们获取线程池。
常用线程池
`1.Executors.newFixedThreadPool();`
执行长期任务性能好,创建一个线程池,一池有N个固定的线程,有固定的线程数的线程
2.Executors.newSingleThreadExecutor();
一个任务一个任务的执行,一池一线程
3.Executors.newCachedThreadPool();
执行很多短期异步任务,线程池根据需要创建新线程,但在先前构建的线程可用时将重用它们。可扩容,遇强则强。
4.newScheduledThreadPool();
执行定时任务的池子
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
相当于无限创建线程了。
实际用的
自己手写。原因:
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,规避资源消耗。
说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool;
允许的请求队列长度为Integer的最大值,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool和ScheduledThreadPool
允许的创建线程数量为Integer最大值,可能会创建大量的线程,从而导致OOM。
详情参考《阿里开发手册-泰山版》
5.线程池的五种状态
1.running
能接受新的任务以及处理已经添加的任务
2.shutdown
不接受新的任务,可以处理已经添加的任务
3.stop
不接受新任务,不处理已经添加的任务,并且中断正在处理的任务
4.tidying
所有的任务已经终止,ctl记录的任务数量为0,ctl负责记录线程池的运行状态与活动线程数量
5.terminated
线程池彻底终止,则线程池转变为terminated状态。
线程池的底层实际上是用integer的高3位来表示这个线程池的状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//由这个源码可见,他是吧int的高三位记录线程池的生命状态,低29位记录当前工作线程数。
private static final int COUNT_BITS = Integer.SIZE - 3;
//COUNT_BITS:32-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=1111 1111 1111 1111 1111 1111 1111 1111
//RUNNING =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
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
pool.shutdown(),pool.shutdownNow();
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
q.drainTo(taskList);
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
他俩都是直接打断正在执行的任务(PS:shutdown其实是尝试停止所有的正在执行或暂停任务的线程),但是shutdown会分出一个线程将等待队列的任务执行完,而shutdownNow是直接将等待队列的任务都不执行了,给你返回回来。
/**
* Interrupts threads that might be waiting for tasks (as
* indicated by not being locked) so they can check for
* termination or configuration changes. Ignores
* SecurityExceptions (in which case some threads may remain
* uninterrupted).
*
* @param onlyOne If true, interrupt at most one worker. This is
* called only from tryTerminate when termination is otherwise
* enabled but there are still other workers. In this case, at
* most one waiting worker is interrupted to propagate shutdown
* signals in case all threads are currently waiting.
* Interrupting any arbitrary thread ensures that newly arriving
* workers since shutdown began will also eventually exit.
* To guarantee eventual termination, it suffices to always
* interrupt only one idle worker, but shutdown() interrupts all
* idle workers so that redundant workers exit promptly, not
* waiting for a straggler task to finish.
*/
这是shutdown里面的一段注释,大概就是尝试打断当前正在执行的任务,但是要拿出一个线程执行等待队列的任务,当只有这一个线程并且任务执行完了,才会退出。
execute()和submit()
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
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);
}
源码的注释写的太友好了,我都懒得翻译成中文了。
/**
* Checks if a new worker can be added with respect to current
* pool state and the given bound (either core or maximum). If so,
* the worker count is adjusted accordingly, and, if possible, a
* new worker is created and started, running firstTask as its
* first task. This method returns false if the pool is stopped or
* eligible to shut down. It also returns false if the thread
* factory fails to create a thread when asked. If the thread
* creation fails, either due to the thread factory returning
* null, or due to an exception (typically OutOfMemoryError in
* Thread.start()), we roll back cleanly.
*
* @param firstTask the task the new thread should run first (or
* null if none). Workers are created with an initial first task
* (in method execute()) to bypass queuing when there are fewer
* than corePoolSize threads (in which case we always start one),
* or when the queue is full (in which case we must bypass queue).
* Initially idle threads are usually created via
* prestartCoreThread or to replace other dying workers.
*
* @param core if true use corePoolSize as bound, else
* maximumPoolSize. (A boolean indicator is used here rather than a
* value to ensure reads of fresh values after checking other pool
* state).
* @return true if successful
*/
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
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);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
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(Runnable firstTask) {
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);
}
由此可见,实际上调用线程工厂创建的线程是在worker的构造器里面创建的,他传递的是当前对象,这样就会执行他的run方法,run里面执行的方法传递的还是他的当前对象。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// 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) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
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);
}
}
实际上里面执行的是task.run()。
其实一追源码,我去?submit底层原来也是调用的execute,但是人家还返回了一个FutureTask句柄,我们拿到这个句柄以后就可以通过他的get方法获取结果,这个get方法是阻塞式的,当获取到结果就可以把结果交给需要的线程然后唤醒他继续执行。
FutureTask
追一追futureTask的源码~~~~
由源码可知,FutureTask类实现了Runnable接口。所以FutureTask类一定实现了run方法,我们去查看他的run(),run()大概的流程就是,定义一个叫做result的变量接收结果,他会判断你是否已经执行了run方法,如果执行了,把result的值设置进去,所以调用get才可以获取到值。这里面执行的是c的call方法,c是什么?c=callable,看一下构造器,发现callable实际上是一个成员变量,将构造器传进来的callable接口赋值给了成员变量。
假如传递的是一个Runnable接口呢?
继续追源码~~~
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
实际上它是将runnable接口转化成了callable接口,只不过返回值为空而已。
join()
先来看个demo
public static void main(String[] args) throws InterruptedException {
final ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10000; i++){
Thread thread = new Thread(() -> {
list.add(new Random().nextInt());
});
thread.start();
//thread.join();
}
System.out.println(list.size());
}
输出结果竟然小于10000,当我把注释的join方法打开,输出结果就等于一万了,为啥呢?
首先小于10000是因为主线程结束了,但是还有几个线程没执行完,也就是还没添加到list。所以list的size小于10000.
为啥加了join又等于10000了呢?追一下源码~~~~
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
从源码可以看出,join()底层时调用了wait()的,也就是主线程会等待所有线程执行结束之后(准确的说应该是执行到join()的地方)在继续执行。
补充一个概念:句柄
句柄实际上是C语言的,Java中用句柄来表示一个引用或者指针,java万事万物皆对象,大部分对对象的操作都是通过操作引用来操作对象的,我们把这个引用就叫做句柄。