定时任务系列(4)-Quartz创建Scheduler调度器核心原理

Quartz所有都起始于SchedulerFactory, SchedulerFactory有两个是实现类StdSchedulerFactoryDirectSchedulerFactory

  • StdSchedulerFactory:使用一组属性(java.util.Properties)来创建和初始化Quartz Scheduler。属性通常存储在文件中并从文件中加载,但也可以由程序创建并直接传递到工厂。
  • DirectSchedulerFactory:以更加程序化的方式创建其Scheduler实例,即所有的参数都需要传递。

这里以StdSchedulerFactory为例描述Quartz初始化的整个过程:

    public static Scheduler getDefaultScheduler() throws SchedulerException {
        StdSchedulerFactory fact = new StdSchedulerFactory();

        return fact.getScheduler();
    }

StdSchedulerFactory中提供一个getDefaultScheduler的方法,获取一个默认的Scheduler。
这里直接new一个StdSchedulerFactory,并调用了getScheduler方法。

	public Scheduler getScheduler() throws SchedulerException {
        if (cfg == null) {
            //第一步加载配置文件,System的properties覆盖前面的配置
            initialize();
        }
       // 本地存储Schedule任务,注:SchedulerRepository是单例模式
        SchedulerRepository schedRep = SchedulerRepository.getInstance();

        Scheduler sched = schedRep.lookup(getSchedulerName());

        if (sched != null) {
            if (sched.isShutdown()) {
                schedRep.remove(getSchedulerName());
            } else {
                return sched;
            }
        }
        //开始很多初始化对象
        sched = instantiate();

        return sched;
    }

获取配置文件

判断PropertiesParser是否为空,不为空就说明已经初始化过,不需要再次初始化。如果没有初始化,直接调用initialize方法初始化,加载配置文件。

	public void initialize() throws SchedulerException {
        // short-circuit if already initialized
        // 如果已初始化
        if (cfg != null) {
            return;
        }
        if (initException != null) {
            throw initException;
        }

        String requestedFile = System.getProperty(PROPERTIES_FILE);
        String propFileName = requestedFile != null ? requestedFile
                : "quartz.properties";
        File propFile = new File(propFileName);

        Properties props = new Properties();

        InputStream in = null;

        try {
            if (propFile.exists()) {
                try {
                    if (requestedFile != null) {
                        propSrc = "specified file: '" + requestedFile + "'";
                    } else {
                        propSrc = "default file in current working dir: 'quartz.properties'";
                    }

                    in = new BufferedInputStream(new FileInputStream(propFileName));
                    props.load(in);

                } catch (IOException ioe) {
                    initException = new SchedulerException("Properties file: '"
                            + propFileName + "' could not be read.", ioe);
                    throw initException;
                }
            } else if (requestedFile != null) {
                in =
                    Thread.currentThread().getContextClassLoader().getResourceAsStream(requestedFile);

                if(in == null) {
                    initException = new SchedulerException("Properties file: '"
                        + requestedFile + "' could not be found.");
                    throw initException;
                }

                propSrc = "specified file: '" + requestedFile + "' in the class resource path.";

                in = new BufferedInputStream(in);
                try {
                    props.load(in);
                } catch (IOException ioe) {
                    initException = new SchedulerException("Properties file: '"
                            + requestedFile + "' could not be read.", ioe);
                    throw initException;
                }

            } else {
                propSrc = "default resource file in Quartz package: 'quartz.properties'";

                ClassLoader cl = getClass().getClassLoader();
                if(cl == null)
                    cl = findClassloader();
                if(cl == null)
                    throw new SchedulerConfigException("Unable to find a class loader on the current thread or class.");

                in = cl.getResourceAsStream(
                        "quartz.properties");

                if (in == null) {
                    in = cl.getResourceAsStream(
                            "/quartz.properties");
                }
                if (in == null) {
                    in = cl.getResourceAsStream(
                            "org/quartz/quartz.properties");
                }
                if (in == null) {
                    initException = new SchedulerException(
                            "Default quartz.properties not found in class path");
                    throw initException;
                }
                try {
                    props.load(in);
                } catch (IOException ioe) {
                    initException = new SchedulerException(
                            "Resource properties file: 'org/quartz/quartz.properties' "
                                    + "could not be read from the classpath.", ioe);
                    throw initException;
                }
            }
        } finally {
            if(in != null) {
                try { in.close(); } catch(IOException ignore) { /* ignore */ }
            }
        }

        initialize(overrideWithSysProps(props));
    }

再次判断是否初始化,有异常抛异常,并开始读取配置文件数据,默认quartz.properties,配置key:org.quartz.properties,总而言之,这段代码就是找出quartz的配置文件,读取到Properties中。

	private Properties overrideWithSysProps(Properties props) {
        Properties sysProps = null;
        try {
            sysProps = System.getProperties();
        } catch (AccessControlException e) {
            getLog().warn(
                "Skipping overriding quartz properties with System properties " +
                "during initialization because of an AccessControlException.  " +
                "This is likely due to not having read/write access for " +
                "java.util.PropertyPermission as required by java.lang.System.getProperties().  " +
                "To resolve this warning, either add this permission to your policy file or " +
                "use a non-default version of initialize().",
                e);
        }

        if (sysProps != null) {
            props.putAll(sysProps);
        }

        return props;
    }

再来看一下最后的overrideWithSysProps方法,顾名思义,通过获取系统变量覆盖配置文件中的配置,所以这里系统配置优先级高于配置文件。

    public void initialize(Properties props) throws SchedulerException {
        if (propSrc == null) {
            propSrc = "an externally provided properties instance.";
        }

        this.cfg = new PropertiesParser(props);
    }

除了overrideWithSysProps,最后哦一行还有一个initialize方法,这里就是初始化PropertiesParser全局对象。
至此,加载配置文件的过程全部结束。

创建Scheduler

继续回到 getScheduler方法
SchedulerRepository类似一个本地缓存,Map类型存储<SchedulerName,Scheduler>,先判断SchedulerRepository存不存在,不存在就新增,再判断SchedulerRepository中是否存在当前需要加载的Scheduler,如果存在,并且不是停止的就不用往下走了,直接返回。如果是停止的,需要先从缓存中移除,然后进行初始化加载。接下来进入核心的instantiate方法。

核心方法instantiate

1、获取基础配置

        if (cfg == null) {
            initialize();
        }

        if (initException != null) {
            throw initException;
        }

        JobStore js = null;
        ThreadPool tp = null;
        QuartzScheduler qs = null;
        DBConnectionManager dbMgr = null;
        String instanceIdGeneratorClass = null;
        Properties tProps = null;
        String userTXLocation = null;
        boolean wrapJobInTx = false;
        boolean autoId = false;
        long idleWaitTime = -1;
        long dbFailureRetry = 15000L; // 15 secs
        String classLoadHelperClass;
        String jobFactoryClass;
        ThreadExecutor threadExecutor;

        SchedulerRepository schedRep = SchedulerRepository.getInstance();

        // Get Scheduler Properties
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //Scheduler名
        String schedName = cfg.getStringProperty(PROP_SCHED_INSTANCE_NAME,
                "QuartzScheduler");
        //调度线程名
        String threadName = cfg.getStringProperty(PROP_SCHED_THREAD_NAME,
                schedName + "_QuartzSchedulerThread");
        //实例的id,默认 NON_CLUSTERED
        String schedInstId = cfg.getStringProperty(PROP_SCHED_INSTANCE_ID,
                DEFAULT_INSTANCE_ID);
        //实例ID生成策略
        if (schedInstId.equals(AUTO_GENERATE_INSTANCE_ID)) {
            autoId = true;
            instanceIdGeneratorClass = cfg.getStringProperty(
                    PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS,
                    "org.quartz.simpl.SimpleInstanceIdGenerator");
        }
        else if (schedInstId.equals(SYSTEM_PROPERTY_AS_INSTANCE_ID)) {
            autoId = true;
            instanceIdGeneratorClass = 
                    "org.quartz.simpl.SystemPropertyInstanceIdGenerator";
        }
        //org.quartz.scheduler.userTransactionURL
        userTXLocation = cfg.getStringProperty(PROP_SCHED_USER_TX_URL,
                userTXLocation);
        if (userTXLocation != null && userTXLocation.trim().length() == 0) {
            userTXLocation = null;
        }
        //org.quartz.scheduler.classLoadHelper.class
        classLoadHelperClass = cfg.getStringProperty(
                PROP_SCHED_CLASS_LOAD_HELPER_CLASS,
                "org.quartz.simpl.CascadingClassLoadHelper");
        wrapJobInTx = cfg.getBooleanProperty(PROP_SCHED_WRAP_JOB_IN_USER_TX,
                wrapJobInTx);
        //org.quartz.scheduler.jobFactory.class
        jobFactoryClass = cfg.getStringProperty(
                PROP_SCHED_JOB_FACTORY_CLASS, null);
        //org.quartz.scheduler.idleWaitTime
        idleWaitTime = cfg.getLongProperty(PROP_SCHED_IDLE_WAIT_TIME,
                idleWaitTime);
        if(idleWaitTime > -1 && idleWaitTime < 1000) {
            throw new SchedulerException("org.quartz.scheduler.idleWaitTime of less than 1000ms is not legal.");
        }
        //org.quartz.scheduler.dbFailureRetryInterval 数据库操作失败重试间隔,默认15秒
        dbFailureRetry = cfg.getLongProperty(PROP_SCHED_DB_FAILURE_RETRY_INTERVAL, dbFailureRetry);
        if (dbFailureRetry < 0) {
            throw new SchedulerException(PROP_SCHED_DB_FAILURE_RETRY_INTERVAL + " of less than 0 ms is not legal.");
        }
        //是否生成守护线程
        boolean makeSchedulerThreadDaemon =
            cfg.getBooleanProperty(PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON);
        //org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer 上下文加载器
        boolean threadsInheritInitalizersClassLoader =
            cfg.getBooleanProperty(PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD);
        //org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow
        long batchTimeWindow = cfg.getLongProperty(PROP_SCHED_BATCH_TIME_WINDOW, 0L);
        //org.quartz.scheduler.batchTriggerAcquisitionMaxCount
        int maxBatchSize = cfg.getIntProperty(PROP_SCHED_MAX_BATCH_SIZE, 1);

        boolean interruptJobsOnShutdown = cfg.getBooleanProperty(PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN, false);
        boolean interruptJobsOnShutdownWithWait = cfg.getBooleanProperty(PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT, false);
        /*
        注:JMX(Java Management Extensions,即Java管理扩展)是Java平台上为应用程序、设备、系统等植入管理功能的框架。
        JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
        */
        boolean jmxExport = cfg.getBooleanProperty(PROP_SCHED_JMX_EXPORT);
        String jmxObjectName = cfg.getStringProperty(PROP_SCHED_JMX_OBJECT_NAME);
        
        boolean jmxProxy = cfg.getBooleanProperty(PROP_SCHED_JMX_PROXY);
        String jmxProxyClass = cfg.getStringProperty(PROP_SCHED_JMX_PROXY_CLASS);
        /* RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)
            是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。
            远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。
        */
        boolean rmiExport = cfg.getBooleanProperty(PROP_SCHED_RMI_EXPORT, false);
        boolean rmiProxy = cfg.getBooleanProperty(PROP_SCHED_RMI_PROXY, false);
        String rmiHost = cfg.getStringProperty(PROP_SCHED_RMI_HOST, "localhost");
        int rmiPort = cfg.getIntProperty(PROP_SCHED_RMI_PORT, 1099);
        int rmiServerPort = cfg.getIntProperty(PROP_SCHED_RMI_SERVER_PORT, -1);
        String rmiCreateRegistry = cfg.getStringProperty(
                PROP_SCHED_RMI_CREATE_REGISTRY,
                QuartzSchedulerResources.CREATE_REGISTRY_NEVER);
        String rmiBindName = cfg.getStringProperty(PROP_SCHED_RMI_BIND_NAME);

        if (jmxProxy && rmiProxy) {
            throw new SchedulerConfigException("Cannot proxy both RMI and JMX.");
        }
        
        boolean managementRESTServiceEnabled = cfg.getBooleanProperty(MANAGEMENT_REST_SERVICE_ENABLED, false);
        String managementRESTServiceHostAndPort = cfg.getStringProperty(MANAGEMENT_REST_SERVICE_HOST_PORT, "0.0.0.0:9889");

        Properties schedCtxtProps = cfg.getPropertyGroup(PROP_SCHED_CONTEXT_PREFIX, true);

以上代码就是解析properties中的配置,如果没有配置,获取默认值。
RMI和JMX有关远程调用的暂时不考虑。

2、获取JobFactory配置

        JobFactory jobFactory = null;
        if(jobFactoryClass != null) {
            try {
                jobFactory = (JobFactory) loadHelper.loadClass(jobFactoryClass).getConstructor()
                        .newInstance();
            } catch (Exception e) {
                throw new SchedulerConfigException(
                        "Unable to instantiate JobFactory class: "
                                + e.getMessage(), e);
            }

            tProps = cfg.getPropertyGroup(PROP_SCHED_JOB_FACTORY_PREFIX, true);
            try {
                setBeanProps(jobFactory, tProps);
            } catch (Exception e) {
                initException = new SchedulerException("JobFactory class '"
                        + jobFactoryClass + "' props could not be configured.", e);
                throw initException;
            }
        }

解析配org.quartz.scheduler.jobFactory将其下的属性都赋值给JobFactory

3、获取InstanceIdGenerator配置

 InstanceIdGenerator instanceIdGenerator = null;
        if(instanceIdGeneratorClass != null) {
            try {
                instanceIdGenerator = (InstanceIdGenerator) loadHelper.loadClass(instanceIdGeneratorClass).getConstructor()
                    .newInstance();
            } catch (Exception e) {
                throw new SchedulerConfigException(
                        "Unable to instantiate InstanceIdGenerator class: "
                        + e.getMessage(), e);
            }

            tProps = cfg.getPropertyGroup(PROP_SCHED_INSTANCE_ID_GENERATOR_PREFIX, true);
            try {
                setBeanProps(instanceIdGenerator, tProps);
            } catch (Exception e) {
                initException = new SchedulerException("InstanceIdGenerator class '"
                        + instanceIdGeneratorClass + "' props could not be configured.", e);
                throw initException;
            }
        }

解析org.quartz.scheduler.instanceIdGenerator下的属性,并赋值InstanceIdGenerator。

4、获取线程池配置

		// Get ThreadPool Properties
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //从配置中获取线程池的类路径,如果没有的话就拿SimpleThreadPool
        String tpClass = cfg.getStringProperty(PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName());

        if (tpClass == null) {
            initException = new SchedulerException(
                    "ThreadPool class not specified. ");
            throw initException;
        }
        //初始化主线程池SimpleThreadPool
        try {
            tp = (ThreadPool) loadHelper.loadClass(tpClass).getConstructor().newInstance();
        } catch (Exception e) {
            initException = new SchedulerException("ThreadPool class '"
                    + tpClass + "' could not be instantiated.", e);
            throw initException;
        }
        tProps = cfg.getPropertyGroup(PROP_THREAD_POOL_PREFIX, true);
        try {
            setBeanProps(tp, tProps);
        } catch (Exception e) {
            initException = new SchedulerException("ThreadPool class '"
                    + tpClass + "' props could not be configured.", e);
            throw initException;
        }

获取org.quartz.threadPool.class的配置,不配置默认SimpleThreadPool。获取org.quartz.threadPool下的配置,并将属性赋值给ThreadPool.

5、获取存储类型配置

        // Get JobStore Properties
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // 获取任务存储中心配置,创建任务存储中心 默认使用RAMJobStore内存存储
        String jsClass = cfg.getStringProperty(PROP_JOB_STORE_CLASS,
                RAMJobStore.class.getName());

        if (jsClass == null) {
            initException = new SchedulerException(
                    "JobStore class not specified. ");
            throw initException;
        }
        //初始化存储中心
        try {
            js = (JobStore) loadHelper.loadClass(jsClass).getConstructor().newInstance();
        } catch (Exception e) {
            initException = new SchedulerException("JobStore class '" + jsClass
                    + "' could not be instantiated.", e);
            throw initException;
        }

        SchedulerDetailsSetter.setDetails(js, schedName, schedInstId);

        tProps = cfg.getPropertyGroup(PROP_JOB_STORE_PREFIX, true, new String[] {PROP_JOB_STORE_LOCK_HANDLER_PREFIX});
        try {
            setBeanProps(js, tProps);
        } catch (Exception e) {
            initException = new SchedulerException("JobStore class '" + jsClass
                    + "' props could not be configured.", e);
            throw initException;
        }

        if (js instanceof JobStoreSupport) {
            // Install custom lock handler (Semaphore)
            String lockHandlerClass = cfg.getStringProperty(PROP_JOB_STORE_LOCK_HANDLER_CLASS);
            if (lockHandlerClass != null) {
                try {
                    Semaphore lockHandler = (Semaphore)loadHelper.loadClass(lockHandlerClass).getConstructor().newInstance();

                    tProps = cfg.getPropertyGroup(PROP_JOB_STORE_LOCK_HANDLER_PREFIX, true);

                    // If this lock handler requires the table prefix, add it to its properties.
                    if (lockHandler instanceof TablePrefixAware) {
                        tProps.setProperty(
                                PROP_TABLE_PREFIX, ((JobStoreSupport)js).getTablePrefix());
                        tProps.setProperty(
                                PROP_SCHED_NAME, schedName);
                    }

                    try {
                        setBeanProps(lockHandler, tProps);
                    } catch (Exception e) {
                        initException = new SchedulerException("JobStore LockHandler class '" + lockHandlerClass
                                + "' props could not be configured.", e);
                        throw initException;
                    }

                    ((JobStoreSupport)js).setLockHandler(lockHandler);
                    getLog().info("Using custom data access locking (synchronization): " + lockHandlerClass);
                } catch (Exception e) {
                    initException = new SchedulerException("JobStore LockHandler class '" + lockHandlerClass
                            + "' could not be instantiated.", e);
                    throw initException;
                }
            }
        }

根据org.quartz.jobStore.class获取任务存储中心配置,默认使用RAMJobStore内存存储
如果配置为数据库存储JobStoreSupport,再看是否配置自定义锁处理器

6、获取数据源配置

      	// Set up any DataSources
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        String[] dsNames = cfg.getPropertyGroups(PROP_DATASOURCE_PREFIX);
        for (int i = 0; i < dsNames.length; i++) {
            PropertiesParser pp = new PropertiesParser(cfg.getPropertyGroup(
                    PROP_DATASOURCE_PREFIX + "." + dsNames[i], true));
            //自定义的数据库连接器
            String cpClass = pp.getStringProperty(PROP_CONNECTION_PROVIDER_CLASS, null);

            // custom connectionProvider...
            if(cpClass != null) {
                ConnectionProvider cp = null;
                try {
                    cp = (ConnectionProvider) loadHelper.loadClass(cpClass).getConstructor().newInstance();
                } catch (Exception e) {
                    initException = new SchedulerException("ConnectionProvider class '" + cpClass
                            + "' could not be instantiated.", e);
                    throw initException;
                }

                try {
                    // remove the class name, so it isn't attempted to be set
                    pp.getUnderlyingProperties().remove(
                            PROP_CONNECTION_PROVIDER_CLASS);
                    //数据库连接池
                    if (cp instanceof PoolingConnectionProvider) {
                        populateProviderWithExtraProps((PoolingConnectionProvider)cp, pp.getUnderlyingProperties());
                    } else {
                        //其他非jdbc接口,例如:JND、Weblogic
                        setBeanProps(cp, pp.getUnderlyingProperties());
                    }
                    cp.initialize();
                } catch (Exception e) {
                    initException = new SchedulerException("ConnectionProvider class '" + cpClass
                            + "' props could not be configured.", e);
                    throw initException;
                }

                dbMgr = DBConnectionManager.getInstance();
                dbMgr.addConnectionProvider(dsNames[i], cp);
            } else {
                String dsJndi = pp.getStringProperty(PROP_DATASOURCE_JNDI_URL, null);

                if (dsJndi != null) {
                    boolean dsAlwaysLookup = pp.getBooleanProperty(
                            PROP_DATASOURCE_JNDI_ALWAYS_LOOKUP);
                    String dsJndiInitial = pp.getStringProperty(
                            PROP_DATASOURCE_JNDI_INITIAL);
                    String dsJndiProvider = pp.getStringProperty(
                            PROP_DATASOURCE_JNDI_PROVDER);
                    String dsJndiPrincipal = pp.getStringProperty(
                            PROP_DATASOURCE_JNDI_PRINCIPAL);
                    String dsJndiCredentials = pp.getStringProperty(
                            PROP_DATASOURCE_JNDI_CREDENTIALS);
                    Properties props = null;
                    if (null != dsJndiInitial || null != dsJndiProvider
                            || null != dsJndiPrincipal || null != dsJndiCredentials) {
                        props = new Properties();
                        if (dsJndiInitial != null) {
                            props.put(PROP_DATASOURCE_JNDI_INITIAL,
                                    dsJndiInitial);
                        }
                        if (dsJndiProvider != null) {
                            props.put(PROP_DATASOURCE_JNDI_PROVDER,
                                    dsJndiProvider);
                        }
                        if (dsJndiPrincipal != null) {
                            props.put(PROP_DATASOURCE_JNDI_PRINCIPAL,
                                    dsJndiPrincipal);
                        }
                        if (dsJndiCredentials != null) {
                            props.put(PROP_DATASOURCE_JNDI_CREDENTIALS,
                                    dsJndiCredentials);
                        }
                    }
                    JNDIConnectionProvider cp = new JNDIConnectionProvider(dsJndi,
                            props, dsAlwaysLookup);
                    dbMgr = DBConnectionManager.getInstance();
                    dbMgr.addConnectionProvider(dsNames[i], cp);
                } else {
                    String poolingProvider = pp.getStringProperty(PoolingConnectionProvider.POOLING_PROVIDER);
                    String dsDriver = pp.getStringProperty(PoolingConnectionProvider.DB_DRIVER);
                    String dsURL = pp.getStringProperty(PoolingConnectionProvider.DB_URL);

                    if (dsDriver == null) {
                        initException = new SchedulerException(
                                "Driver not specified for DataSource: "
                                        + dsNames[i]);
                        throw initException;
                    }
                    if (dsURL == null) {
                        initException = new SchedulerException(
                                "DB URL not specified for DataSource: "
                                        + dsNames[i]);
                        throw initException;
                    }
                    // we load even these "core" providers by class name in order to avoid a static dependency on
                    // the c3p0 and hikaricp libraries
                    if(poolingProvider != null && poolingProvider.equals(PoolingConnectionProvider.POOLING_PROVIDER_HIKARICP)) {
                        cpClass = "org.quartz.utils.HikariCpPoolingConnectionProvider";
                    }
                    else {
                        cpClass = "org.quartz.utils.C3p0PoolingConnectionProvider";
                    }
                    log.info("Using ConnectionProvider class '" + cpClass + "' for data source '" + dsNames[i] + "'");

                    try {
                        ConnectionProvider cp = null;
                        try {
                            Constructor<?> constructor = loadHelper.loadClass(cpClass).getConstructor(Properties.class);
                            cp = (ConnectionProvider) constructor.newInstance(pp.getUnderlyingProperties());
                        } catch (Exception e) {
                            initException = new SchedulerException("ConnectionProvider class '" + cpClass
                                    + "' could not be instantiated.", e);
                            throw initException;
                        }
                        dbMgr = DBConnectionManager.getInstance();
                        dbMgr.addConnectionProvider(dsNames[i], cp);

                        // Populate the underlying C3P0/HikariCP data source pool properties
                        populateProviderWithExtraProps((PoolingConnectionProvider)cp, pp.getUnderlyingProperties());
                    } catch (Exception sqle) {
                        initException = new SchedulerException(
                                "Could not initialize DataSource: " + dsNames[i],
                                sqle);
                        throw initException;
                    }
                }

            }

        }

获取org.quartz.dataSource数据源配置,可以支持配置多个数据源。
可配置自定义的数据库连接池,默认支持C3P0C3p0PoolingConnectionProvider和HikaricpHikariCpPoolingConnectionProvider两种数据连接池。
还可以配置非JDBC的连接器:JNDIConnectionProviderWeblogicConnectionProvider等等。

如果没有配置自定义的数据库连接器:先查询是否有JNDI相关配置,如果有,则加载JNDI相关数据源。如果没有JNDI相关配置,直接加载数据源配置,内置数据库连接器为C3p0PoolingConnectionProvider

7、获取调度器插件配置

        // Set up any SchedulerPlugins
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //注册插件
        String[] pluginNames = cfg.getPropertyGroups(PROP_PLUGIN_PREFIX);
        SchedulerPlugin[] plugins = new SchedulerPlugin[pluginNames.length];
        for (int i = 0; i < pluginNames.length; i++) {
            Properties pp = cfg.getPropertyGroup(PROP_PLUGIN_PREFIX + "."
                    + pluginNames[i], true);

            String plugInClass = pp.getProperty(PROP_PLUGIN_CLASS, null);

            if (plugInClass == null) {
                initException = new SchedulerException(
                        "SchedulerPlugin class not specified for plugin '"
                                + pluginNames[i] + "'");
                throw initException;
            }
            SchedulerPlugin plugin = null;
            try {
                plugin = (SchedulerPlugin)
                        loadHelper.loadClass(plugInClass).newInstance();
            } catch (Exception e) {
                initException = new SchedulerException(
                        "SchedulerPlugin class '" + plugInClass
                                + "' could not be instantiated.", e);
                throw initException;
            }
            try {
                setBeanProps(plugin, pp);
            } catch (Exception e) {
                initException = new SchedulerException(
                        "JobStore SchedulerPlugin '" + plugInClass
                                + "' props could not be configured.", e);
                throw initException;
            }

            plugins[i] = plugin;
        }

根据org.quartz.plugin获取插件配置,可以注册多个插件,org.quartz.plugin.XXXX.class获取插件的类路径。

8、获取任务监听器配置

        // Set up any JobListeners
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // 注册JOB监听器
        Class<?>[] strArg = new Class[] { String.class };
        String[] jobListenerNames = cfg.getPropertyGroups(PROP_JOB_LISTENER_PREFIX);
        JobListener[] jobListeners = new JobListener[jobListenerNames.length];
        for (int i = 0; i < jobListenerNames.length; i++) {
            Properties lp = cfg.getPropertyGroup(PROP_JOB_LISTENER_PREFIX + "."
                    + jobListenerNames[i], true);

            String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null);

            if (listenerClass == null) {
                initException = new SchedulerException(
                        "JobListener class not specified for listener '"
                                + jobListenerNames[i] + "'");
                throw initException;
            }
            JobListener listener = null;
            try {
                listener = (JobListener)
                       loadHelper.loadClass(listenerClass).getConstructor().newInstance();
            } catch (Exception e) {
                initException = new SchedulerException(
                        "JobListener class '" + listenerClass
                                + "' could not be instantiated.", e);
                throw initException;
            }
            try {
                Method nameSetter = null;
                try { 
                    nameSetter = listener.getClass().getMethod("setName", strArg);
                }
                catch(NoSuchMethodException ignore) { 
                    /* do nothing */ 
                }
                if(nameSetter != null) {
                    nameSetter.invoke(listener, new Object[] {jobListenerNames[i] } );
                }
                setBeanProps(listener, lp);
            } catch (Exception e) {
                initException = new SchedulerException(
                        "JobListener '" + listenerClass
                                + "' props could not be configured.", e);
                throw initException;
            }
            jobListeners[i] = listener;
        }

根据org.quartz.jobListener获取监听器配置,类似插件配置.

9、获取触发器监听器配置

        // Set up any TriggerListeners
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // 注册Trigger监听器
        String[] triggerListenerNames = cfg.getPropertyGroups(PROP_TRIGGER_LISTENER_PREFIX);
        TriggerListener[] triggerListeners = new TriggerListener[triggerListenerNames.length];
        for (int i = 0; i < triggerListenerNames.length; i++) {
            Properties lp = cfg.getPropertyGroup(PROP_TRIGGER_LISTENER_PREFIX + "."
                    + triggerListenerNames[i], true);

            String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null);

            if (listenerClass == null) {
                initException = new SchedulerException(
                        "TriggerListener class not specified for listener '"
                                + triggerListenerNames[i] + "'");
                throw initException;
            }
            TriggerListener listener = null;
            try {
                listener = (TriggerListener)
                       loadHelper.loadClass(listenerClass).newInstance();
            } catch (Exception e) {
                initException = new SchedulerException(
                        "TriggerListener class '" + listenerClass
                                + "' could not be instantiated.", e);
                throw initException;
            }
            try {
                Method nameSetter = null;
                try { 
                    nameSetter = listener.getClass().getMethod("setName", strArg);
                }
                catch(NoSuchMethodException ignore) { /* do nothing */ }
                if(nameSetter != null) {
                    nameSetter.invoke(listener, new Object[] {triggerListenerNames[i] } );
                }
                setBeanProps(listener, lp);
            } catch (Exception e) {
                initException = new SchedulerException(
                        "TriggerListener '" + listenerClass
                                + "' props could not be configured.", e);
                throw initException;
            }
            triggerListeners[i] = listener;
        }

根据org.quartz.triggerListener获取Trigger监听器配置,其他类似插件配置。

10、获取执行线程配置

        // Get ThreadExecutor Properties
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        String threadExecutorClass = cfg.getStringProperty(PROP_THREAD_EXECUTOR_CLASS);
        if (threadExecutorClass != null) {
            tProps = cfg.getPropertyGroup(PROP_THREAD_EXECUTOR, true);
            try {
                threadExecutor = (ThreadExecutor) loadHelper.loadClass(threadExecutorClass).getConstructor().newInstance();
                log.info("Using custom implementation for ThreadExecutor: " + threadExecutorClass);

                setBeanProps(threadExecutor, tProps);
            } catch (Exception e) {
                initException = new SchedulerException(
                        "ThreadExecutor class '" + threadExecutorClass + "' could not be instantiated.", e);
                throw initException;
            }
        } else {
            log.info("Using default implementation for ThreadExecutor 使用 ThreadExecutor 的默认实现");
            threadExecutor = new DefaultThreadExecutor();
        }

根据org.quartz.threadExecutor.class获取执行线程的类路径,如果没有配置,就使用ThreadExecutor 的默认实现DefaultThreadExecutor。默认的执行器就一个方法,就是启动一个线程。代码如下:

public class DefaultThreadExecutor implements ThreadExecutor {

    public void initialize() {
    }

    public void execute(Thread thread) {
        thread.start();
    }

}

至此,所有的配置都获取完了,开始正式的调度了 Fire everything up

Fire everything up

1、创建JobRunShellFactory

JobRunShellFactory jrsf = null; // Create correct run-shell factory...

 if (userTXLocation != null) {
     UserTransactionHelper.setUserTxLocation(userTXLocation);
 }

 if (wrapJobInTx) { //fasle
     jrsf = new JTAJobRunShellFactory();
 } else {
     jrsf = new JTAAnnotationAwareJobRunShellFactory();
 }

从前面获取的org.quartz.scheduler.userTransactionURL配置事物的地址
org.quartz.scheduler.wrapJobExecutionInUserTransaction获取数据判断是否将任务包装在事物中。

注意JTAJobRunShellFactoryJTAAnnotationAwareJobRunShellFactory这两个的区别:
JTAAnnotationAwareJobRunShellFactory 如果任务的类上存在注解ExecuteInJTATransaction,就新增一个带超时时间的JTAJobRunShell,否则就新建一个普通的JTAJobRunShell

	public JobRunShell createJobRunShell(TriggerFiredBundle bundle)
            throws SchedulerException {
        ExecuteInJTATransaction jtaAnnotation = ClassUtils.getAnnotation(bundle.getJobDetail().getJobClass(), ExecuteInJTATransaction.class);
        if(jtaAnnotation == null)
            return new JobRunShell(scheduler, bundle);
        else {
            int timeout = jtaAnnotation.timeout();
            if (timeout >= 0) {
                return new JTAJobRunShell(scheduler, bundle, timeout);
            } else {
                return new JTAJobRunShell(scheduler, bundle);
            }
        }
    }

2、设置任务调度唯一标识

			//设置任务调度器唯一标识符
			 if (autoId) {
			     try {
			       schedInstId = DEFAULT_INSTANCE_ID;
			       if (js.isClustered()) {
			           //如果JobStore是集群,必须要有调度器唯一标识符生成器
			           schedInstId = instanceIdGenerator.generateInstanceId();
			       }
			     } catch (Exception e) {
			         getLog().error("Couldn't generate instance Id!", e);
			         throw new IllegalStateException("Cannot run without an instance id.");
			     }
			 }

3、数据库存储类型为JobStoreSupport的处理

			 if (js instanceof JobStoreSupport) {
			     JobStoreSupport jjs = (JobStoreSupport)js;
			     jjs.setDbRetryInterval(dbFailureRetry);
			     //线程是否继承Initalizers类加载器
			     if(threadsInheritInitalizersClassLoader)
			         jjs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
			     
			     jjs.setThreadExecutor(threadExecutor);
			 }

4、创建调度器资源类

            //开始创建调度器资源类
            QuartzSchedulerResources rsrcs = new QuartzSchedulerResources();
            rsrcs.setName(schedName);
            rsrcs.setThreadName(threadName);
            rsrcs.setInstanceId(schedInstId);
            rsrcs.setJobRunShellFactory(jrsf);
            rsrcs.setMakeSchedulerThreadDaemon(makeSchedulerThreadDaemon);
            rsrcs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
            rsrcs.setBatchTimeWindow(batchTimeWindow);
            rsrcs.setMaxBatchSize(maxBatchSize);
            rsrcs.setInterruptJobsOnShutdown(interruptJobsOnShutdown);
            rsrcs.setInterruptJobsOnShutdownWithWait(interruptJobsOnShutdownWithWait);
            rsrcs.setJMXExport(jmxExport);
            rsrcs.setJMXObjectName(jmxObjectName);

            if (managementRESTServiceEnabled) { //false
                ManagementRESTServiceConfiguration managementRESTServiceConfiguration = new ManagementRESTServiceConfiguration();
                managementRESTServiceConfiguration.setBind(managementRESTServiceHostAndPort);
                managementRESTServiceConfiguration.setEnabled(managementRESTServiceEnabled);
                rsrcs.setManagementRESTServiceConfiguration(managementRESTServiceConfiguration);
            }
    
            if (rmiExport) {    //false
                rsrcs.setRMIRegistryHost(rmiHost);
                rsrcs.setRMIRegistryPort(rmiPort);
                rsrcs.setRMIServerPort(rmiServerPort);
                rsrcs.setRMICreateRegistryStrategy(rmiCreateRegistry);
                rsrcs.setRMIBindName(rmiBindName);
            }
            // ThreadPool tp 是线程池,这里是对setInstanceName,setInstanceId赋值
            SchedulerDetailsSetter.setDetails(tp, schedName, schedInstId);
            // 在调度器资源类中放入线程执行器
            rsrcs.setThreadExecutor(threadExecutor);
            //线程执行器初始化,默认空方法
            threadExecutor.initialize();
            //设置线程池
            rsrcs.setThreadPool(tp);
            if(tp instanceof SimpleThreadPool) {
                if(threadsInheritInitalizersClassLoader)
                    ((SimpleThreadPool)tp).setThreadsInheritContextClassLoaderOfInitializingThread(threadsInheritInitalizersClassLoader);
            }
            /**
             * 工作线程的初始化(重要)
             */
            tp.initialize();
            tpInited = true;
    
            rsrcs.setJobStore(js);
    
            // add plugins
            for (int i = 0; i < plugins.length; i++) {
                rsrcs.addSchedulerPlugin(plugins[i]);
            }

QuartzSchedulerResources类似上下文存储的数据,存储需要调度的参数。
这一段最重要的是工作线程的初始化,这里以ThreadPool的默认实现SimpleThreadPool说明:

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 {
            //查询threadGroup树形找到Main线程ThreadGroup
            // 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);
        }
    }

前半段的部分是初始化一个ThreadGroup,本来Java是不推荐ThreadGroup的,但是这里通过ThreadGroup可以对工作线程进行分组,可能出于管理线程的角度。这个方法最核心的就是createWorkerThreads方法,先看这个方法:

	protected List<WorkerThread> createWorkerThreads(int createCount) {
        workers = new LinkedList<WorkerThread>();
        for (int i = 1; i<= createCount; ++i) {
            String threadPrefix = getThreadNamePrefix();
            if (threadPrefix == null) {
                threadPrefix = schedulerInstanceName + "_Worker";
            }
            WorkerThread wt = new WorkerThread(this, threadGroup,
                threadPrefix + "-" + i,
                getThreadPriority(),
                isMakeThreadsDaemons());
            //没有地方调用设置的方法,一直为true
            if (isThreadsInheritContextClassLoaderOfInitializingThread()) {
                wt.setContextClassLoader(Thread.currentThread()
                        .getContextClassLoader());
            }
            workers.add(wt);
        }

        return workers;
    }

这里传入的createCount是系统的逻辑核数。这里的for 循环,创建核数个线程WorkerThread,尽可能利用系统资源,并将新建的线程保存在workers全局变量中。
createWorkerThreads方法结束后,遍历新创建的线程集合workers,全部启动,并存放在availWorkers中,表示可用线程。

注意这里WorkerThread的构造函数,有5个参数,这里的runabe为null。

		/**
         * <p>
         * Create a worker thread and start it. Waiting for the next Runnable,
         * executing it, and waiting for the next Runnable, until the shutdown
         * flag is set.
         * </p>
         */
        WorkerThread(SimpleThreadPool tp, ThreadGroup threadGroup, String name,
                     int prio, boolean isDaemon) {

            this(tp, threadGroup, name, prio, isDaemon, null);
        }

        /**
         * <p>
         * Create a worker thread, start it, execute the runnable and terminate
         * the thread (one time execution).
         * </p>
         */
        WorkerThread(SimpleThreadPool tp, ThreadGroup threadGroup, String name,
                     int prio, boolean isDaemon, Runnable runnable) {

            super(threadGroup, name);
            this.tp = tp;
            this.runnable = runnable;
            if(runnable != null)
                runOnce = true;
            setPriority(prio);
            setDaemon(isDaemon);
        }

5、WorkerThread工作线程调度
上面说到workers线程启动了,现在我们看看WorkerThread线程里的逻辑:
WorkerThreadSimpleThreadPool这个类中的内部类

		/**
         * <p>
         * Loop, executing targets as they are received.
         * 循环,在收到目标时执行它们。
         * </p>
         */
        @Override
        public void run() {
            boolean ran = false;
            
            while (run.get()) {
                try {
                    synchronized(lock) {
                        while (runnable == null && run.get()) {
                            lock.wait(500);
                        }

                        if (runnable != null) {
                            ran = true;
                            //启动任务
                            runnable.run();
                        }
                    }
                } catch (InterruptedException unblock) {
                    // do nothing (loop will terminate if shutdown() was called
                    try {
                        getLog().error("Worker thread was interrupt()'ed.", unblock);
                    } catch(Exception e) {
                        // ignore to help with a tomcat glitch
                    }
                } catch (Throwable exceptionInRunnable) {
                    try {
                        getLog().error("Error while executing the Runnable: ",
                            exceptionInRunnable);
                    } catch(Exception e) {
                        // ignore to help with a tomcat glitch
                    }
                } finally {
                    synchronized(lock) {
                        runnable = null;
                    }
                    // repair the thread in case the runnable mucked it up...
                    if(getPriority() != tp.getThreadPriority()) {
                        setPriority(tp.getThreadPriority());
                    }

                    if (runOnce) {
                           run.set(false);
                        clearFromBusyWorkersList(this);
                    } else if(ran) {
                        ran = false;
                        //线程重新加入可用线程
                        makeAvailable(this);
                    }

                }
            }

            //if (log.isDebugEnabled())
            try {
                getLog().debug("WorkerThread is shut down.");
            } catch(Exception e) {
                // ignore to help with a tomcat glitch
            }
        }

首先,run.get()默认是原子类true,如果没有shutdown的话,会一直处于循环中。
进入同步代码块后,上面一步说到这里的runnable为null。如果一直没有线程进来的话,这里就会一直死循环,等待500ms继续循环。如果有线程进来的话,直接启动线程。目前生成Schedule阶段,不会存在有业务线程的情况,这个方法的后续,我们之后再说。

6、实例化QuartzScheduler

          /**
           * 实例化 其中包含运行的线程
           */
          qs = new QuartzScheduler(rsrcs, idleWaitTime, dbFailureRetry);
          qsInited = true;
  
          // Create Scheduler ref...// 关键点,创建出Scheduler,默认是StdScheduler
          Scheduler scheduler = instantiate(rsrcs, qs);
  
          // set job factory if specified
          if(jobFactory != null) {
              qs.setJobFactory(jobFactory);
          }

这段代码直接创建出了QuartzScheduler,这个也是一个非常重要的类。它将之前创建的资源类QuartzSchedulerResources传入。
接下来看一下QuartzScheduler的构造方法:

    public QuartzScheduler(QuartzSchedulerResources resources, long idleWaitTime, @Deprecated long dbRetryInterval)
        throws SchedulerException {
        this.resources = resources;
        if (resources.getJobStore() instanceof JobListener) {
            addInternalJobListener((JobListener)resources.getJobStore());
        }

        /**
         * 创建调度线程 并执行
         */
        this.schedThread = new QuartzSchedulerThread(this, resources);
        ThreadExecutor schedThreadExecutor = resources.getThreadExecutor();
        schedThreadExecutor.execute(this.schedThread);
        if (idleWaitTime > 0) {
            this.schedThread.setIdleWaitTime(idleWaitTime);
        }

        jobMgr = new ExecutingJobsManager();
        addInternalJobListener(jobMgr);
        errLogger = new ErrorLogger();
        addInternalSchedulerListener(errLogger);

        signaler = new SchedulerSignalerImpl(this, this.schedThread);
        
        getLog().info("Quartz Scheduler v." + getVersion() + " created.");
    }

第一段判断JobStore如果是JobListener,就将JobStore加入监听器中,但是我看了几种默认的JobStore实现,包含RAMJobStoreJobStoreSupportJobStoreTXJobStoreCMT,其中都没有包含监听器接口的,所以这里先不管。
第二段是重点,实例化了一个QuartzSchedulerThread,并由我们前面创建的线程执行器DefaultThreadExecutor执行的。
同时:这里new了一个信号调度器SchedulerSignalerImpl,这个后面会用到。
先看看QuartzSchedulerThread的构造函数:

    QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs, boolean setDaemon, int threadPrio) {
        super(qs.getSchedulerThreadGroup(), qsRsrcs.getThreadName());
        this.qs = qs;
        this.qsRsrcs = qsRsrcs;
        this.setDaemon(setDaemon);
        if(qsRsrcs.isThreadsInheritInitializersClassLoadContext()) {
            log.info("QuartzSchedulerThread Inheriting ContextClassLoader of thread: " + Thread.currentThread().getName());
            this.setContextClassLoader(Thread.currentThread().getContextClassLoader());
        }

        this.setPriority(threadPrio);

        // start the underlying thread, but put this object into the 'paused'
        // state
        // so processing doesn't start yet...
        //暂停
        paused = true;
        //主线程停止标志
        halted = new AtomicBoolean(false);
    }

这里将QuartzSchedulerQuartzSchedulerResources存入,然后设置paused为true,设置halted为false。这两个变量是控制主线程暂停和停止的标志,paused为true表示,目前启动阶段暂时暂停主线程,halted为false表示主线程未停止,暂停可以重启,但是停止就不能重启了。

7、QuartzSchedulerThread主线程调度
进入QuartzSchedulerThread线程的run方法:

		/*
         //暂停
         paused = true;
         //主线程停止标志
         halted = new AtomicBoolean(false);
         */
        while (!halted.get()) {
            while (paused && !halted.get()) {
                 try {
                     // wait until togglePause(false) is called...
                     //等到 togglePause(false) 被调用...
                     log.info("QuartzSchedulerThread 等到 togglePause(false) 被调用..." + LocalDateTime.now());
                     sigLock.wait(1000L);
                 } catch (InterruptedException ignore) {
                 }

                 // reset failure counter when paused, so that we don't
                 // wait again after unpausing
                 acquiresFailed = 0;
             }

             // 省略不执行的方法。。。。
           }
        } // while (!halted)

        // drop references to scheduler stuff to aid garbage collection...
        qs = null;
        qsRsrcs = null;

如果主线程不停止,while会一直执行,不停的调度。
进入同步代码块,获取sigLock锁,前面说到paused=true,halted=false,所以这里会进入while循环中,等待1s后继续判断。这里的注释写到:等到 togglePause(false) 被调用…
抢先体验一下这个方法都在干啥:

 	void togglePause(boolean pause) {
        synchronized (sigLock) {
            paused = pause;

            if (paused) {
                signalSchedulingChange(0);
            } else {
                sigLock.notifyAll();
            }
        }
    }

这里传入false的话,暂停开关变成false,主线程就可以继续调度了。
这里一定要加锁,防止多线程同时调度此方法。
最后唤醒所有等待sigLock锁的线程,进行线程抢占调度。

8、注册自定义插件、监听器
再回到第6点,实例化QuartzScheduler后,开始注册自定义插件、监听器

        // Initialize plugins now that we have a Scheduler instance.
        for (int i = 0; i < plugins.length; i++) {
            plugins[i].initialize(pluginNames[i], scheduler, loadHelper);
        }

        // add listeners
        for (JobListener jobListener : jobListeners) {
            qs.getListenerManager().addJobListener(jobListener, EverythingMatcher.allJobs());
        }
        for (TriggerListener triggerListener : triggerListeners) {
            qs.getListenerManager().addTriggerListener(triggerListener, EverythingMatcher.allTriggers());
        }

        // set scheduler context data...
        for(Object key: schedCtxtProps.keySet()) {
            String val = schedCtxtProps.getProperty((String) key);    
            scheduler.getContext().put((String)key, val);
        }

9、初始化JobStore

        // fire up job store, and runshell factory
        // 开始初始化JobStore
        js.setInstanceId(schedInstId);
        js.setInstanceName(schedName);
        js.setThreadPoolSize(tp.getPoolSize());
        //初始化锁 这里前面创建qs的最后一步设置的
        js.initialize(loadHelper, qs.getSchedulerSignaler());
        //其实就是在jrsf注入scheduler变量
        jrsf.initialize(scheduler);
        //这里做了远程绑定,如果没有的话会直接跳过(rmi,jmx)
        qs.initialize();

        getLog().info(
                "Quartz scheduler '" + scheduler.getSchedulerName()
                        + "' initialized from " + propSrc);

        getLog().info("Quartz scheduler version: " + qs.getVersion());

        // prevents the repository from being garbage collected 防止存储库被垃圾收集
        qs.addNoGCObject(schedRep);
        // prevents the db manager from being garbage collected 防止数据库管理器被垃圾收集
        if (dbMgr != null) {
            qs.addNoGCObject(dbMgr);
        }
        //调用程序库SchedulerRepository中新添加scheduler
        schedRep.bind(scheduler);
        return scheduler;

先看一下代码中的初始化锁:
RAMJobStore中直接初始化了signaler
在``中初始化了loadHelper和signaler
如果用户没有显示在配置文件中指定锁处理器,如果是集群,就上数据库锁,查询锁的sql如下:

SELECT * FROM {0}LOCKS WITH (UPDLOCK,ROWLOCK) WHERE " + COL_SCHEDULER_NAME + " = {1} AND LOCK_NAME = ?

并将sql封装成StdRowLockSemaphore:内部基于数据库的锁处理程序,用于提供线程资源锁定,以防止资源同时被多个线程更改。
如果没有上数据库锁,默认封装一个SimpleSemaphore:内部内存锁处理程序,用于提供线程资源锁定,以保护资源不被多个线程同时更改。

结束语

到此为止,scheduler就被创建出来了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值