本期带大家来分析线程池ThreadPoolExecutor的源码,在看本文之前最好要知道线程池的执行逻辑和核心参数,这样更方便从源码入手分析流程。
重要的属性
//高3位表示线程池运行状态,除去高3位后的低位,表示当前线程池中线程的数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//表示在ctl中存放线程数量的位数
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程的最大个数 00011111111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) -1;
//以下是线程池的运行状态
//11100000000000000000000000000000 是一个负数
private static final int RUNNING = -1 << COUNT_BITS;
//00000000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
//00100000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS;
//01000000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
//01100000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
//任务队列,当线程池的线程数达到核心线程数时,再提交任务就会提交到该队列中
private final BlockingQueue<Runnable> workQueue;
//线程池全局锁,增加worker 减少 worker 时需要持有mainLock , 修改线程池运行状态时,也需要。
private final ReentrantLock mainLock = new ReentrantLock();
//线程池,存放线程的集合
private final HashSet<Worker> workers = new HashSet<Worker>();
//当外部线程调用awaitTermination()方法时,外部线程需要等待线程池状态为TERMINATED,否则加入条件队列
private final Condition termination = mainLock.newCondition();
//记录线程池生命周期内最大线程数
private int largestPoolSize;
//记录线程池所完成的任务总数,当worker退出时会将worker完成的任务累计到该字段上
private long completedTaskCount;
//线程工厂
private volatile ThreadFactory threadFactory;
//拒绝策略
private volatile RejectedExecutionHandler handler;
//核心线程数内的线程是否可以被回收。true 可以,false不可以。
private volatile boolean allowCoreThreadTimeOut;
//空闲线程存活时间,当allowCoreThreadTimeOut == false 时,会维护核心线程数量内的线程存活,超出部分会被超时。
//allowCoreThreadTimeOut == true 核心数量内的线程空闲时也会被回收。
private volatile long keepAliveTime;
//核心线程数
private volatile int corePoolSize;
//最大线程数
private volatile int maximumPoolSize;
//默认的拒绝策略
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
在这里还需要提前知道线程池每一种状态下都能能做什么。
从整型值来看RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED。
RUNNING:线程池初始化状态,这时线程池可以接收新任务的提交
SHUTDOWN:调用shutdown()方法时,线程池由RUNNING变为SHUTDOWN,这时不能接收新任务,但能继续处理已添加的任务。
STOP:调用shutdownNow()方法时,由RUNNING或SHUTDOWN变为STOP,这时不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
TIDYING:当线程池ctl记录的线程数为0时,线程池会变为TIDYING。在SHUTDOWN状态下,任务队列为空并且线程池为空,会由SHUTDOWN变为TIDYING。在STOP状态下,线程池为空,就会由STOP变为TIDYING。
TERMINATED:线程池生命周期结束。在TIDYING状态下,执行完terminated()方法后,会变为TERMINATED。
所以线程池在RUNNING和SHUTDOWN状态时,都是可以执行任务的。
重要的辅助方法
//获取当前线程池运行状态
//CAPACITY: 00011111111111111111111111111111
//~CAPACITY:11100000000000000000000000000000
//这时再回到上面看看线程池状态的值,只有高3位才有1,所以&可以计算出线程池状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
//获取当前线程池线程数量,计算同上
private static int workerCountOf(int c) { return c & CAPACITY; }
//计算ctl的值,rs是线程池状态,wc是线程数
//rs只有高3位里面才可能包含1 wc除了高3位里面才可能包含1
private static int ctlOf(int rs, int wc) { return rs | wc; }
//c:ctl s:线程池某个状态
//比较当前线程池状态,是否小于某个状态
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
//比较当前线程池状态,是否大于等于某个状态
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
//是否是RUNNING, SHUTDOWN==0 RUNNING为负数
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
//cas 将ctl加1,表示线程数加1
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
//cas 将ctl减1,表示线程数减1
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
//让ctl减1
private void decrementWorkerCount() {
//这里会一直重试,直到成功为止。
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
内部类Worker
在ThreadPoolExecutor内还有个最重要的内部类Worker,还记得前面存储线程的集合HashSet吗?存储的类型就是Worker
//Worker继承了AQS实现了独占模式,在前面分析ReentrantLock的时候已经分析过了
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
//Worker内持有的线程
final Thread thread;
//第一个任务,Worker启动后会优先执行firstTask,执行完后才会去任务队列取任务
Runnable firstTask;
//当前Worker完成的所有任务数量
volatile long completedTasks;
//firstTask可以为null
Worker(Runnable firstTask) {
//设置AQS锁状态
setState(-1);
this.firstTask = firstTask;
//使用线程工厂创建线程,并将当前Worker实例做为Runnable传入
this.thread = getThreadFactory().newThread(this);
}
//由于在构造方法内创建线程时,将自身做为了Runnable,所以线程会回调这个run()
public void run() {
//外部类,也就是ThreadPoolExecutor的方法
runWorker(this);
}
//以下是AQS相关方法,不多介绍
//是否被独占
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(); }
}
到这里有关线程池的重要前提就分析完毕,我们实际使用线程池的时候通常是用execute()或submit()提交任务,我们从这两个方法开始入手。
submit()提交任务
线程池的submit()方法是在#AbstractExecutorService中,有3个重载方法
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
//还是调用execute()
execute(ftask);
return ftask;
}
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;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
但不管是哪一个,都是将Runnable或Callable封装成FutureTask由execute()进行提交任务,所以重点在execute()方法内。
execute()提交任务
#ThreadPoolExecutor
public void execute(Runnable command) {
//非空判断
if (command == null)
throw new NullPointerException();
//获取ctl的值,高3位表示线程池状态,低29位表示当前线程池内线程数量
int c = ctl.get();
//workerCountOf() 获取当前线程数量
//如果小于核心线程数
if (workerCountOf(c) < corePoolSize) {
//addWorker()创建worker对象,将command作为firstTask
if (addWorker(command, true))
//创建成功会返回
return;
//执行到这,说明创建失败了
c = ctl.get();
}
//执行到这,几种情况?
//1.线程数>=核心线程数
//2.addWorker()创建失败
//isRunning()判断线程池是否是运行态
//workQueue.offer()向任务队列提交任务
if (isRunning(c) && workQueue.offer(command)) {
//执行到这,说明是运行态并且提交任务队列成功
//recheck保存ctl的最新值
int recheck = ctl.get();
//remove() 删除任务
if (!isRunning(recheck) && remove(command))
//如果执行到这,说明任务刚提交到队列后,线程池状态已经发生改变,不是Running
//并且在任务队列里删除了任务
//执行拒绝策略
reject(command);
//如果当前线程数为0
else if (workerCountOf(recheck) == 0)
//执行到这,说明线程池状态是Running,但是内部没有线程了
//创建一个Worker
//因为刚刚将任务添加到任务队列里,所以创建Worker传的任务是null
addWorker(null, false);
}
//执行到这是什么情况?
//1.线程池不是Running
//2.任务队列添加失败
//继续创建Worker,在addWorker内部会去判断线程数是否达到最大线程数
else if (!addWorker(command, false))
//拒绝策略
reject(command);
}
从execute()方法内就可以看出线程池是优先创建线程达到核心线程数,再去提交任务队列的,还有最大线程数的作用没有看到,这个逻辑会在addWorker内出现。
接下来我们重点分析addWorker的过程
addWorker()
//firstTask:提交的任务
//core:是否为核心线程数
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
//自旋,判断当前线程池是否允许创建线程
for (;;) {
//获取ctl
int c = ctl.get();
//获取线程池的状态
int rs = runStateOf(c);
//rs>=SHUTDOWN:说明不是Running
//!(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()) 注意取反了
//这三个条件只要任意一个为false,第二个条件整体来看就为true
if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
//执行到这,可能是线程池状态为STOP,TIDYING,TERMINATED之一
//或提交任务为null
//或任务队列为空
return false;
//以上的代码就是用来判断当前线程池的状态,是否允许添加线程
//内部自旋,获取创建线程的机会
for (;;) {
//获取当前线程数量
int wc = workerCountOf(c);
//判断线程数是否超过核心线程数或最大线程数
if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
//说明无法添加线程,返回false
return false;
//执行到这说明可以继续添加,先cas增加线程数
if (compareAndIncrementWorkerCount(c))
//成功,说明可以创建线程,跳出外部自旋
break retry;
//cas失败,获取最新的ctl
c = ctl.get();
//判断当前线程池状态是否发生过变化
if (runStateOf(c) != rs)
//发生了变化就回到外部自旋里,继续判断当前线程池状态是否可以创建线程
continue retry;
}
}
//表示创建的worker是否启动
boolean workerStarted = false;
//表示创建的worker是否添加到线程池
boolean workerAdded = false;
//worker的引用
Worker w = null;
try {
//创建Worker,并将提交的任务赋值给firstTask
w = new Worker(firstTask);
//将Worker内部的线程引用赋值给t
final Thread t = w.thread;
//判断线程是否为null,主要是因为用户可能自定义线程工厂,在Worker的构造内会调用线程工厂创建线程
//自定义线程工厂可能会有bug,返回null
if (t != null) {
//获取全局锁
final ReentrantLock mainLock = this.mainLock;
//加锁
mainLock.lock();
try {
//获取线程池状态
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
//t.isAlive(),线程启动后会返回true,防止线程工厂创建线程后就调用start()
if (t.isAlive())
throw new IllegalThreadStateException();
//将worker添加到线程池
workers.add(w);
//获取线程数量
int s = workers.size();
//说明当前线程数是线程池生命周期内最高值
if (s > largestPoolSize)
//更新最高值
largestPoolSize = s;
//设置添加成功
workerAdded = true;
}
} finally {
//解锁
mainLock.unlock();
}
//判断是否添加成功
if (workerAdded) {
//启动线程
t.start();
//设置worker启动成功
workerStarted = true;
}
}
} finally {
//worker启动失败
if (!workerStarted)
//进入添加失败的逻辑
addWorkerFailed(w);
}
//返回创建的worker是否启动成功
return workerStarted;
}
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//worker不为null
if (w != null)
//从线程池内移除当前worker
workers.remove(w);
//将worker数-1
decrementWorkerCount();
//在shutdown()方法会讲
tryTerminate();
} finally {
mainLock.unlock();
}
}
addWorker()会在内部进行两种类型的判断,先去判断当前线程池状态是否能够添加线程,再去判断核心线程数和最大线程数的限制。当条件满足后会创建Worker,然后加锁,将当前Worker加入到线程池并启动,Worker内部的线程启动最终会回调到Worker的run()方法里。
以上就是线程池提交任务的逻辑,接下来就要去看线程启动后的执行逻辑了。
Worker的run()
线程工厂创建线程时,将Worker作为Runnable传入进去,所以线程启动后会回调到Worker的run()方法。
public void run() {
runWorker(this);
}
//w:就是当前启动的worker
final void runWorker(Worker w) {
//获取当前执行线程,就是worker内部的线程引用
Thread wt = Thread.currentThread();
//获取worker的firstTask
Runnable task = w.firstTask;
//将worker的firstTask置空
w.firstTask = null;
//这里为什么调用worker的解锁?可以再回到Worker内部看一下,其实是在tryRelease里面将state和独占线程清空
w.unlock();
//标识线程是否突然退出
boolean completedAbruptly = true;
try {
//循环条件
//task != null 第一次是指firstTask不为null
//(task = getTask()) != null 从任务队列取出的任务不为null
while (task != null || (task = getTask()) != null) {
//加锁,其实是线程池处于shutdown时会判断当前worker的状态,根据独占锁判断worker是否空闲
w.lock();
//runStateAtLeast(ctl.get(), STOP) 线程池处于STOP,TIDYING,TERMINATION状态
//这个if就是用来保证,线程池正在停止,就需要中断线程
//如果没有,就要确保线程不被中断,并且要清除线程的中断标记位
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)))
&& !wt.isInterrupted())
wt.interrupt();
try {
//留给子类实现的钩子方法
beforeExecute(wt, task);
//异常,如果在执行方法发现异常,就不为null
Throwable thrown = null;
try {
//执行task的run()方法
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清空
task = null;
//当前worker执行的任务数量++
w.completedTasks++;
//解锁,清空独占状态
w.unlock();
}
}
//上面的while条件不满足时,会执行到这
//正常退出,将标识位设为false
completedAbruptly = false;
} finally {
//1.正常退出 completedAbruptly==false
//2.异常退出 completedAbruptly==true
processWorkerExit(w, completedAbruptly);
}
}
接下来拆分runWorker()内部的两个方法,getTask()和processWorkerExit()
getTask()从任务队列取任务
private Runnable getTask() {
//标识当前线程获取任务是否超时
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
//获取ctl的值
int c = ctl.get();
//获取当前线程池运行状态
int rs = runStateOf(c);
//rs >= SHUTDOWN 说明为非RUNNING状态
//rs >= STOP 说明当前线程池最低处于STOP状态
//workQueue.isEmpty() 任务队列为空
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//减少worker数量
decrementWorkerCount();
return null;
}
//执行到这,说明
//1.线程池是RUNNING
//2.线程池是SHUTDOWN,但任务队列不为空
//获取线程数量
int wc = workerCountOf(c);
// true表示当前线程允许超时,false不允许
//allowCoreThreadTimeOut:是否允许核心线程超时
//wc > corePoolSize:判断线程数量是否超过核心线程数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//wc > maximumPoolSize 是否超过最大线程数 可能是外部重新设置了最大线程数的参数
//timed && timedOut 用来判断线程获取任务是否超时
//wc > 1 说明线程池还有其他线程,当前线程可以回收
//workQueue.isEmpty() 任务队列为空
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) {
//cas减少线程数,可能存在其他线程比当前线程先退出的情况
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;
}
}
}
processWorkerExit()线程退出
//completedAbruptly:true 异常退出 ,false 正常退出
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
//正常退出的话,在getTask()内部会将线程数-1,异常退出需要在这里进行调整
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//将当前worker执行的任务数,累加到线程池的completedTaskCount
completedTaskCount += w.completedTasks;
//从线程池移除当前worker
workers.remove(w);
} finally {
mainLock.unlock();
}
//尝试终止线程池,后面说
tryTerminate();
//获取ctl
int c = ctl.get();
//判断线程池状态至少为RUNNING或SHUTDOWN
if (runStateLessThan(c, STOP)) {
//正常退出
if (!completedAbruptly) {
//表示线程池能够持有的最少线程数
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
//如果最少线程数为0,并且线程池内还有任务
if (min == 0 && ! workQueue.isEmpty())
//保证线程池内至少还有一个线程执行任务
min = 1;
//判断当前线程数是否>=min
if (workerCountOf(c) >= min)
//说明线程数已经足够,返回
return; // replacement not needed
}
//执行到这几种情况?
//1.线程异常退出,需要重新开启线程执行任务
//2.任务队列还有任务,但线程数量不够
//3.线程数量未达到核心线程数
addWorker(null, false);
}
}
以上就是线程池内线程的运行逻辑,当线程启动后,优先执行Worker的firstTask,执行完后会去任务队列取任务,取任务前会进行一些线程池状态的校验和线程池核心参数的校验,然后才返回任务给线程继续执行。直到任务队列也为空,线程就会执行退出的逻辑,退出也需要经过线程池状态和核心参数的校验,判断是否需要退出线程。如果执行任务期间发生异常,也会进入线程退出的逻辑,不过当前线程退出前会向线程池重新添加一个线程执行任务,然后再退出。
接下来该分析一下线程池结束的方法shutdown()和shutdownNow()了。
shutdown()
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
//加锁
mainLock.lock();
try {
//不是重点
checkShutdownAccess();
//设置线程池状态为SHUTDOWN,内部就是自旋+cas修改,自行查看
advanceRunState(SHUTDOWN);
//中断空闲线程
interruptIdleWorkers();
//钩子方法
onShutdown();
} finally {
//解锁
mainLock.unlock();
}
//尝试终止线程池
tryTerminate();
}
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
//onlyOne:false 中断所有线程 , true 中断所有线程
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//遍历所有worker
for (Worker w : workers) {
//获取worker的线程
Thread t = w.thread;
//!t.isInterrupted()==true 说明当前线程还未中断
//w.tryLock()==true 说明当前线程处于空闲
if (!t.isInterrupted() && w.tryLock()) {
try {
//中断当前线程
t.interrupt();
} catch (SecurityException ignore) {
} finally {
//释放woker独占锁
w.unlock();
}
}
//onlyOne==true 只中断一个线程就退出
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
final void tryTerminate() {
//自旋
for (;;) {
//获取ctl
int c = ctl.get();
//isRunning(c)==true 当前线程池为RUNNING
//runStateAtLeast(c, TIDYING)==true 说明当前已经有其他线程在执行TIDYING到TERMINATED状态了
//runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty() SHUTDOWN状态并且任务队列不为空
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
//当前线程直接返回
return;
//以上的条件都不满足
//1.线程池状态为STOP
//2.线程池状态为SHUTDOWN,任务队列也为空
//线程数量不为0
if (workerCountOf(c) != 0) { // Eligible to terminate
//中断一个空闲线程
//空闲线程会在阻塞队列的take()和poll()方法空闲
//唤醒后的线程会在getTask()方法返回,然后执行退出逻辑的时候调用tryTerminate()
//最终空闲线程都会在此处退出
interruptIdleWorkers(ONLY_ONE);
return;
}
//执行到这说明什么?
//线程数量已经为0,执行到这的是最后一个退出的线程
//因为在退出逻辑里面,是先将线程数-1的,所以最后一个线程会执行到这
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//cas修改线程池状态为TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
//钩子方法
terminated();
} finally {
//设置状态为TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
//唤醒调用了awaiTermination()的线程
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
shutdownNow()
public List<Runnable> shutdownNow() {
//存储未处理的任务
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
//设置线程池状态为STOP
advanceRunState(STOP);
//中断线程池中所有线程
interruptWorkers();
//获取未处理的任务,内部逻辑比较简单,自行查看
tasks = drainQueue();
} finally {
mainLock.unlock();
}
//尝试终止线程池
tryTerminate();
//返回所有未处理的任务
return tasks;
}
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//遍历所有worker
for (Worker w : workers)
//如果worker内的Thread不是空闲就中断
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
awaitTermination()
这个方法是由外部线程调用,当线程池变为TERMINATED的状态时会唤醒等待在此的线程。
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (;;) {
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
if (nanos <= 0)
return false;
//Condition条件队列,相当于synchronized,获取锁的线程调用了wait()
nanos = termination.awaitNanos(nanos);
}
} finally {
mainLock.unlock();
}
}
以上就是线程池核心源码的分析。难点主要是在判断线程池状态的逻辑上,要分很多种情况去考虑,不过只要明白线程池各状态分别能处理什么事情,加上理解了线程池的执行逻辑,源码也不是特别难分析的。