概述
quartz启动后有多个线程同时在跑。启动时会启动主线程、集群线程、检漏线程、工作线程。主线程负责查询到需要触发的线程,并放入到线程队列。
一、主线程QuartzScheduleThread
关于QuartzScheduleThread是quartz启动时开始启动,用于trigger的获取、触发,并放入到线程池中执行。
二、线程池SimpleThreadPool
线程池的初使化:new 线程并放入线程池的链表中。
执行线程:把任务放到一个线程中执行。
线程结束:修改线程状态。
线程池关闭:每个线程关闭,正在执行的线程等线程执行完了再关闭。
三、工作线程WorkThread
线程池中执行的工作线程,可以通过配置文件quartz.properties来配置大小。
JobRunShell实现runnable接口,放入到workThread下执行。
源码分析
1、线程池接口
/*
* Copyright 2001-2009 Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
package org.quartz.spi;
import org.quartz.SchedulerConfigException;
/**
* <p>
* The interface to be implemented by classes that want to provide a thread
* pool for the <code>{@link org.quartz.core.QuartzScheduler}</code>'s use.
* </p>
*
* <p>
* <code>ThreadPool</code> implementation instances should ideally be made
* for the sole use of Quartz. Most importantly, when the method
* <code>blockForAvailableThreads()</code> returns a value of 1 or greater,
* there must still be at least one available thread in the pool when the
* method <code>runInThread(Runnable)</code> is called a few moments (or
* many moments) later. If this assumption does not hold true, it may
* result in extra JobStore queries and updates, and if clustering features
* are being used, it may result in greater imballance of load.
* </p>
*
* @see org.quartz.core.QuartzScheduler
*
* @author James House
*/
public interface ThreadPool {
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
* <p>
* Execute the given <code>{@link java.lang.Runnable}</code> in the next
* available <code>Thread</code>.
* </p>
*
* <p>
* The implementation of this interface should not throw exceptions unless
* there is a serious problem (i.e. a serious misconfiguration). If there
* are no immediately available threads <code>false</code> should be returned.
* </p>
*
* @return true, if the runnable was assigned to run on a Thread.
*/
boolean runInThread(Runnable runnable);
/**
* <p>
* Determines the number of threads that are currently available in in
* the pool. Useful for determining the number of times
* <code>runInThread(Runnable)</code> can be called before returning
* false.
* </p>
*
* <p>The implementation of this method should block until there is at
* least one available thread.</p>
*
* @return the number of currently available threads
*/
int blockForAvailableThreads();
/**
* <p>
* Must be called before the <code>ThreadPool</code> is
* used, in order to give the it a chance to initialize.
* </p>
*
* <p>Typically called by the <code>SchedulerFactory</code>.</p>
*/
void initialize() throws SchedulerConfigException;
/**
* <p>
* Called by the QuartzScheduler to inform the <code>ThreadPool</code>
* that it should free up all of it's resources because the scheduler is
* shutting down.
* </p>
*/
void shutdown(boolean waitForJobsToComplete);
/**
* <p>Get the current number of threads in the <code>ThreadPool</code>.</p>
*/
int getPoolSize();
/**
* <p>Inform the <code>ThreadPool</code> of the Scheduler instance's Id,
* prior to initialize being invoked.</p>
*
* @since 1.7
*/
void setInstanceId(String schedInstId);
/**
* <p>Inform the <code>ThreadPool</code> of the Scheduler instance's name,
* prior to initialize being invoked.</p>
*
* @since 1.7
*/
void setInstanceName(String schedName);
}
2、默认的线程池实现类SimpleThreadPool和工作线程WorkerThread
重点关注createWorkerThreads()方法和shutDown()方法即可。
在初始化线程池SimpleThreadPool时,会创建并运行所有的工作线程WorkerThread,截取initialize()方法如下:
// create the worker threads and start them
Iterator<WorkerThread> workerThreads = createWorkerThreads(count).iterator();
while(workerThreads.hasNext()) {
WorkerThread wt = workerThreads.next();
wt.start();
availWorkers.add(wt);
}
工作线程WorkerThread在线程池初始化时就开始运行,这听上去像是少了什么。是的,每个工作线程的业务逻辑都还没设置,怎么就开始运行了呢?事实上,每个工作线程都有一个装载业务逻辑的Runnable变量,如果该Runnable变量为null,则该工作线程一直处于轮询-等待状态,直至设置了Runnable变量为止,然后才是调用Runnable#run()方法执行业务逻辑。截取WorkerThread的run方法如下:
synchronized(this) {
while (runnable == null && run.get()) {
this.wait(500);
}
if (runnable != null) {
ran = true;
runnable.run();
}
}
由此我们可以看出,线程池中的工作线程应该分为2种:
- 一种是处于轮询-等待状态的工作线程;
- 另一种是真正在执行业务逻辑的工作线程;
所以,在线程池SimpleThreadPool中,有2个集合变量分别保存这2种工作线程:
private LinkedList<WorkerThread> availWorkers = new LinkedList<WorkerThread>();
private LinkedList<WorkerThread> busyWorkers = new LinkedList<WorkerThread>();
线程池SimpleThreadPool也提供了runInThread(Runnable runnable)方法,用于从空闲的工作线程中,以先进先出的顺序获取一个空闲工作线程,为它设置装载业务逻辑的Runnable变量。截取runInThread()方法的部分代码如下:
if (!isShutdown) {
WorkerThread wt = (WorkerThread)availWorkers.removeFirst();
busyWorkers.add(wt);
wt.run(runnable);
}
因为是以先进先出的顺序获取空闲工作线程,这也就解释了在一些定时任务的日志中,每一次起任务的工作线程是不一样的,而且是有序的。如:
2019-02-03 21:16:05 [ INFO] [DefaultQuartzScheduler_Worker-1] gen.common.CommandRunner:32 out:
2019-02-04 21:15:48 [ INFO] [DefaultQuartzScheduler_Worker-2] gen.common.CommandRunner:32 out:
2019-02-05 21:15:00 [ INFO] [DefaultQuartzScheduler_Worker-3] gen.common.CommandRunner:32 out:
。。。。
2019-02-11 21:23:45 [ INFO] [DefaultQuartzScheduler_Worker-9] gen.common.CommandRunner:32 out:
2019-02-12 21:15:00 [ INFO] [DefaultQuartzScheduler_Worker-10] gen.common.CommandRunner:32 out:
类SimpleThreadPool的源码如下:
/*
* Copyright 2001-2009 Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
package org.quartz.simpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.quartz.SchedulerConfigException;
import org.quartz.spi.ThreadPool;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* <p>
* This is class is a simple implementation of a thread pool, based on the
* <code>{@link org.quartz.spi.ThreadPool}</code> interface.
* </p>
*
* <p>
* <CODE>Runnable</CODE> objects are sent to the pool with the <code>{@link #runInThread(Runnable)}</code>
* method, which blocks until a <code>Thread</code> becomes available.
* </p>
*
* <p>
* The pool has a fixed number of <code>Thread</code>s, and does not grow or
* shrink based on demand.
* </p>
*
* @author James House
* @author Juergen Donnerstag
*/
public class SimpleThreadPool implements ThreadPool {
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Data members.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private int count = -1;
private int prio = Thread.NORM_PRIORITY;
private boolean isShutdown = false;
private boolean handoffPending = false;
private boolean inheritLoader = false;
private boolean inheritGroup = true;
private boolean makeThreadsDaemons = false;
private ThreadGroup threadGroup;
private final Object nextRunnableLock = new Object();
private List<WorkerThread> workers;
private LinkedList<WorkerThread> availWorkers = new LinkedList<WorkerThread>();
private LinkedList<WorkerThread> busyWorkers = new LinkedList<WorkerThread>();
private String threadNamePrefix;
private final Logger log = LoggerFactory.getLogger(getClass());
private String schedulerInstanceName;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Constructors.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
* <p>
* Create a new (unconfigured) <code>SimpleThreadPool</code>.
* </p>
*
* @see #setThreadCount(int)
* @see #setThreadPriority(int)
*/
public SimpleThreadPool() {
}
/**
* <p>
* Create a new <code>SimpleThreadPool</code> with the specified number
* of <code>Thread</code> s that have the given priority.
* </p>
*
* @param threadCount
* the number of worker <code>Threads</code> in the pool, must
* be > 0.
* @param threadPriority
* the thread priority for the worker threads.
*
* @see java.lang.Thread
*/
public SimpleThreadPool(int threadCount, int threadPriority) {
setThreadCount(threadCount);
setThreadPriority(threadPriority);
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public Logger getLog() {
return log;
}
public int getPoolSize() {
return getThreadCount();
}
/**
* <p>
* Set the number of worker threads in the pool - has no effect after
* <code>initialize()</code> has been called.
* </p>
*/
public void setThreadCount(int count) {
this.count = count;
}
/**
* <p>
* Get the number of worker threads in the pool.
* </p>
*/
public int getThreadCount() {
return count;
}
/**
* <p>
* Set the thread priority of worker threads in the pool - has no effect
* after <code>initialize()</code> has been called.
* </p>
*/
public void setThreadPriority(int prio) {
this.prio = prio;
}
/**
* <p>
* Get the thread priority of worker threads in the pool.
* </p>
*/
public int getThreadPriority() {
return prio;
}
//设置线程池中的线程的名字前缀
public void setThreadNamePrefix(String prfx) {
this.threadNamePrefix = prfx;
}
//获取线程池中的线程的名字前缀
public String getThreadNamePrefix() {
if(threadNamePrefix == null) {
threadNamePrefix = schedulerInstanceName + "-SimpleThreadPoolWorker";
}
return threadNamePrefix;
}
/**
* @return Returns the
* threadsInheritContextClassLoaderOfInitializingThread.
*/
public boolean isThreadsInheritContextClassLoaderOfInitializingThread() {
return inheritLoader;
}
/**
* @param inheritLoader
* The threadsInheritContextClassLoaderOfInitializingThread to
* set.
*/
public void setThreadsInheritContextClassLoaderOfInitializingThread(
boolean inheritLoader) {
this.inheritLoader = inheritLoader;
}
public boolean isThreadsInheritGroupOfInitializingThread() {
return inheritGroup;
}
public void setThreadsInheritGroupOfInitializingThread(
boolean inheritGroup) {
this.inheritGroup = inheritGroup;
}
/**
* @return Returns the value of makeThreadsDaemons.
*/
public boolean isMakeThreadsDaemons() {
return makeThreadsDaemons;
}
/**
* @param makeThreadsDaemons
* The value of makeThreadsDaemons to set.
*/
public void setMakeThreadsDaemons(boolean makeThreadsDaemons) {
this.makeThreadsDaemons = makeThreadsDaemons;
}
public void setInstanceId(String schedInstId) {
}
public void setInstanceName(String schedName) {
schedulerInstanceName = schedName;
}
public void initialize() throws SchedulerConfigException {
if(workers != null && workers.size() > 0) // already initialized...
return;
if (count <= 0) {
throw new SchedulerConfigException(
"Thread count must be > 0");
}
if (prio <= 0 || prio > 9) {
throw new SchedulerConfigException(
"Thread priority must be > 0 and <= 9");
}
if(isThreadsInheritGroupOfInitializingThread()) {
threadGroup = Thread.currentThread().getThreadGroup();
} else {
// follow the threadGroup tree to the root thread group.
threadGroup = Thread.currentThread().getThreadGroup();
ThreadGroup parent = threadGroup;
while ( !parent.getName().equals("main") ) {
threadGroup = parent;
parent = threadGroup.getParent();
}
threadGroup = new ThreadGroup(parent, schedulerInstanceName + "-SimpleThreadPool");
if (isMakeThreadsDaemons()) {
threadGroup.setDaemon(true);
}
}
if (isThreadsInheritContextClassLoaderOfInitializingThread()) {
getLog().info(
"Job execution threads will use class loader of thread: "
+ Thread.currentThread().getName());
}
// create the worker threads and start them
Iterator<WorkerThread> workerThreads = createWorkerThreads(count).iterator();
while(workerThreads.hasNext()) {
WorkerThread wt = workerThreads.next();
wt.start();
availWorkers.add(wt);
}
}
//创建工作线程WorkerThread
protected List<WorkerThread> createWorkerThreads(int createCount) {
workers = new LinkedList<WorkerThread>();
for (int i = 1; i<= createCount; ++i) {
//new一个WorkerThread对象,参数分别为线程所在的组,线程名称,线程权重,以及是否后台线程标志
WorkerThread wt = new WorkerThread(this, threadGroup,
getThreadNamePrefix() + "-" + i,
getThreadPriority(),
isMakeThreadsDaemons());
if (isThreadsInheritContextClassLoaderOfInitializingThread()) {
wt.setContextClassLoader(Thread.currentThread()
.getContextClassLoader());
}
workers.add(wt);
}
return workers;
}
/**
* <p>
* Terminate any worker threads in this thread group.
* </p>
*
* <p>
* Jobs currently in progress will complete.
* </p>
*/
public void shutdown() {
shutdown(true);
}
/**
* <p>
* Terminate any worker threads in this thread group.
* </p>
*
* <p>
* Jobs currently in progress will complete.
* </p>
*/
public void shutdown(boolean waitForJobsToComplete) {
synchronized (nextRunnableLock) {
getLog().debug("Shutting down threadpool...");
isShutdown = true;
if(workers == null) // case where the pool wasn't even initialize()ed
return;
// signal each worker thread to shut down
Iterator<WorkerThread> workerThreads = workers.iterator();
while(workerThreads.hasNext()) {
WorkerThread wt = workerThreads.next();
wt.shutdown();
availWorkers.remove(wt);
}
// Give waiting (wait(1000)) worker threads a chance to shut down.
// Active worker threads will shut down after finishing their
// current job.
nextRunnableLock.notifyAll();
if (waitForJobsToComplete == true) {
// wait for hand-off in runInThread to complete...
while(handoffPending) {
try { nextRunnableLock.wait(100); } catch(Throwable t) {}
}
// Wait until all worker threads are shut down
while (busyWorkers.size() > 0) {
WorkerThread wt = (WorkerThread) busyWorkers.getFirst();
try {
getLog().debug(
"Waiting for t