타이밍 작업은 특정 작업을 수행하기 위해 향후 시간 범위를 지정하는 기능입니다. 현재 웹 애플리케이션에서 대부분의 응용 프로그램에는 작업 스케줄링 기능이 있습니다. 그들은 다양한 목소리와 운영 체제에 대한 고유 한 구문과 솔루션을 가지고 있습니다. Windows 운영 체제는이를 작업 계획이라고하며 Linux의 CRON 서비스는이 기능을 제공합니다. 이 기능은 종종 비즈니스 개발 시스템에 관여합니다. 이 채팅은 Java 언어를 사용하여 일상 개발 작업에서 일반적으로 사용되는 타이밍 작업의 사용을 완료하여 모든 사람의 업무와 공부를 돕습니다.
1. 시간이 정한 작업 시나리오
(1) 드라이브 처리 워크 플로
새로운 선불 주문으로 초기화되고 배치됩니다. 지정된 시간 내에 주문이 지불되지 않으면 시간 초과 주문이 닫히는 것으로 간주됩니다. 전자 상거래 시스템에서 널리 사용됩니다. 사용자는 상품을 구매하고 주문을 생성하지만 지불하지 않습니다. 주문이 30 분 이내에 지불되지 않으면 주문이 종결되며 (이 시나리오를 충족하는 사람들의 수는 엄청나고) 수동 개입을 사용하는 것은 불가능합니다.
(2) 시스템 유지 보수
디스패치 작업은 시스템 예외 로그를 얻고 데이터베이스에 몇 가지 주요 포인트 데이터를 저장합니다. 매주 오후 11시 30 분에 데이터베이스에 버려지고 (휴일 및 주중 제외) XML 파일을 생성하여 특정 직원의 이메일 주소로 보냅니다.
(3) 응용 프로그램 내에서 알림 서비스를 제공합니다.
이 시스템은 정기적으로 로그인 사용자가 특정 시간에 관련 작업을 수행하도록 상기시킵니다.
(4) 정시 조정 작업
회사와 3 자 회사 (운영자, 은행 등)는 매일 자정 이후 같은 날에 사업의 조정을 수행하고, 해당 정보 결과 데이터를 담당 관련 사람의 이메일 주소로 보내고 다음 날 근무 시간 동안 불일치 데이터를 처리합니다.
(5) 데이터 통계
많은 데이터 레코드가 있으며, 데이터베이스에서 실시간 읽기 및 쿼리는 일정 시간을 생성하며, 이는 고객 경험 및 성능 요구를위한 것입니다. 따라서 데이터는 매주 (일, 시간)에 요약되어 데이터를 표시 할 때 데이터를 신속하게 제시 할 수 있습니다.
타이밍 작업이 사용되는 많은 시나리오가 있습니다 ... 타이밍 작업이 일상 개발에 실제로 널리 사용되는 것 같습니다 ...
2. 주류 타이밍 작업 기술 타이머 설명
나는 모든 사람들이 이미 java.util.timer에 매우 익숙하다고 생각합니다. 작업 일정을 구현하는 가장 쉬운 방법입니다. 특정 예는 다음과 같습니다.
패키지 com.ibm.scheduler; java.util.timer 가져 오기; java.util.timertask 가져 오기; public class timertest는 timertask {private String jobname = ""; public timertest (String jobname) {super (); this.jobname = jobname; } @override public void run () {system.out.println ( "execute" + jobname); } public static void main (String [] args) {타이머 타이머 = new Timer (); 긴 지연 1 = 1 * 1000; 긴 기간 1 = 1000; // 지금부터 1 초 후에 job1 timer.schedule (new Timertest ( "job1"), Delay1, period1)를 실행합니다. 긴 지연 2 = 2 * 1000; 긴 기간 2 = 2000; // 지금부터 2 초 후에 job2 timer.schedule (new Timertest ( "job2"), Delay2, period2)를 실행합니다. }} /**
출력 결과 :
job1을 실행하십시오
job1을 실행하십시오
job2를 실행하십시오
job1을 실행하십시오
job1을 실행하십시오
job2를 실행하십시오
*/
타이머를 사용하여 작업 스케줄링을 구현하는 핵심 클래스는 타이머와 타이머 스탁입니다. 타이머는 Timertask의 시작 및 간격 실행 시간을 설정하는 데 도움이됩니다. 사용자는 상속 클래스의 Timertask 클래스 만 생성하고 자체 실행 방법을 구현 한 다음 타이머로 버려야합니다. 타이머의 디자인 코어는 작업 목록이자 작업 스레드입니다. 타이머는 수신 된 작업을 작업 목록에 버리고 작업의 초기 실행 시간에 따라 작업 목록을 정렬합니다. 타이머를 만들 때 타이머 스레드가 데몬 스레드가되기 시작합니다. 이 스레드는 모든 작업을 폴링하고 최근에 실행될 작업을 찾은 다음 잠을 자고 있습니다. 가장 최근에 실행되는 시작 시간이 실행되면 TimerThread가 깨어나고 작업이 실행됩니다. 그 후 Timerthread는 가장 최근의 작업을 업데이트하여 실행되고 최대 절전 모드를 계속합니다.
타이머의 장점은 간단하고 사용하기 쉽지만 모든 작업이 동일한 스레드에 의해 예약되므로 모든 작업이 일련의 실행되며 동시에 하나의 작업 만 실행할 수 있습니다. 이전 작업의 지연 또는 예외는 후속 작업에 영향을 미칩니다 (이 지점은주의를 기울여야합니다).
예약 사전
위의 타이머 단점을 고려하여 Java 5는 스레드 풀 디자인을 기반으로 ScheduleDexecutor를 시작했습니다. 설계 아이디어는 스레드 풀의 스레드에 의해 예정된 각 작업이 실행되므로 작업이 동시에 실행되며 서로 방해받지 않습니다. ScheduleExecutor는 작업의 실행 시간이 오면 실제로 스레드를 시작하고 SchedueUedExecutor가 나머지 시간 동안 작업을 폴링하고 있습니다.
package com.ibm.scheduler; import java.util.concurrent.executors; import java.util.concurrent.scheduledexecutorservice; import java.util.concurrent.timeunit; public class schedulecutortest emplements {private string jobname = "; public scheduledexecutortest (string jobname) {super (); this.jobname = jobname; } @override public void run () {system.out.println ( "execute" + jobname); } public static void main (string [] args) {scheduledexecutorservice service = executors.newscheduledthreadpool (10); Long InitialDelay1 = 1; 긴 기간 1 = 1; // 지금부터 1 초 후에 job1 service.scheduleatfixedRate (new ScheduleDexecutorTest ( "job1"), initialDelay1, period1, timeUnit.seconds); Long InitialDelay2 = 1; 긴 지연 2 = 1; // 지금부터 2 초 후에 job2 service를 실행합니다 .SchedulewitHfixedDelay (새 ScheduledExecutortest ( "job2"), InitialDelay2, Delay2, TimeUnit.seconds); }}/** 출력 결과 : jobcute job1execute job1execute job2execute job1execute job1execute job2*/
위의 코드는 ScheduleDelta에서 가장 일반적으로 사용되는 두 가지 스케줄링 방법 인 ScheduleAtfixEdrate 및 ScheduleWithFixedDelay를 보여줍니다. ScheduleAtFixEdrate 각 실행 시간은 이전 작업의 시작 부분에서 뒤로 밀리는 시간 간격입니다. 즉, 각 실행 시간은 다음과 같습니다. InitialDelay, InitialDelay+기간, InitialDelay+2*기간,… ScheduleWithFixedDelay 각 실행 시간 각각의 실행 시간은 이전 작업의 끝에서 다시 푸시하는 시간 간격입니다. 각 실행 시간은 다음과 같습니다. InitialDelay+2*ExecuteTime+2*지연. ScheduLeatFixEdrate는 작업 스케줄링의 고정 시간 간격을 기반으로하며 ScheduLewitHfixedDelay는 각 작업 실행 시간의 길이에 따라 달라지며 작업 예약을위한 고정되지 않은 시간 간격을 기반으로합니다.
ScheduleDexecutor 및 Calendar를 사용하여 복잡한 작업 스케줄링을 구현하십시오
타이머와 ScheduleDexecutor는 시작 시간 및 반복 간격에 따라 작업 일정을 제공 할 수 있으며보다 복잡한 스케줄링 요구 사항에 유능하지 않습니다. 예를 들어, 매주 화요일 16:38:10에 실행되도록 작업을 설정하십시오. 이 기능은 타이머 또는 스케줄링 외환을 사용하여 직접 구현할 수 없지만 캘린더의 도움으로 간접적으로 구현할 수 있습니다.
package com.ibm.scheduler; import java.util.calendar; import java.util.date; import java.util.timertask; import java.util.concurrent.executors; import java.util.concurrent.scheduledexecutorservice; import java.util.timecurrent. TimerTask {private String jobName = ""를 확장합니다. public scheduledexceutortest2 (String jobname) {super (); this.jobname = jobname; } @override public void run () {system.out.println ( "date ="+new date ()+", execute"+jobName); } /** * 현재 현재 시간에서 시작하여 주간에 시작하여 주간, 시간의 시간, * 미세한 시간, 2 차 조건을 충족시키는 최신 시간을 계산합니다. Hour_of_day, Minute, Sec int currentdayofweek = currentDate.get (calendar.day_of_week); int currenthour = currentDate.get (calendar.hour_of_day); int currentminute = currentDate.get (calendar.minute); int currentsecond = currentDate.get (calendar.second); // 입력 조건의 주간이 현재 날짜의 주간보다 작은 경우, Week_of_year는 일주일 부울 주자 = false로 연기해야합니다. if (dayofweek <currentdayofweek) {weeklater = true; } else if (dayofweek == currentdayofweek) {// 입력 조건이 현재 날짜의 주간과 같을 때, 입력 조건의 // 시간이 현재 날짜의 // 전류보다 낮 으면, 주 week_of_year는 1 주일 if (Hourofday <currenthour) {weeklater = true; } else if (hourofday == currentShour) {// 입력 조건이 현재 날짜의 주간, 시간의 시간과 같을 때, // 입력 조건의 1 분 시간이 현재 날짜의 // currentminute보다 낮 으면, 주차 _year는 (minutefhour <currentminute = true) 일주일에 연기해야합니다. } else if (minutefhour == currentsecond) {// 입력 조건이 dayofweek, hourofday, // minutefhour, if 입력 조건의 // If // if 입력 조건의 현재 날짜의 전류보다 적은 경우, // 1 주일까지 1 주일까지 연기해야합니다. }}}}} if (Weeklater) {// 현재 날짜에 1 주일에 일주일 date.set (calendar.week_of_year, currentweekofyear + 1); } // 입력 조건의 값으로 현재 날짜의 Day_of_week, Hour_of_day, Minute, Minute, SET INTER currentDate.set (calendar.day_of_week, dayofweek); currentDate.set (calendar.hour_of_day, unhourdofday); currentDate.set (calendar.minute, minutefofhour); currentDate.set (calendar.second, secondofminite); Current Date를 반환합니다. } public static void main (string [] args)은 예외를 던집니다. // 현재 시간 캘린더를 가져옵니다. currentDate = calendar.getInstance (); long currentDatelong = currentDate.getTime (). getTime (); System.out.println ( "현재 날짜 =" + currentDate.getTime (). toString ()); // 조건 캘린더를 충족하는 마지막 실행 시간을 계산합니다. lecrentDate = test .getEarLiestDate (currentDate, 3, 16, 38, 10); Longer ExerentDateLong = ageenTate.getTime (). getTime (); System.out.println ( "초기 날짜 =" + exerentDate.getTime (). toString ()); // 현재 시간에서 마지막 실행 시간까지의 시간 간격을 계산합니다. 긴 지연 = ExeralDatelong -CurrentDatelong; // 실행 기간 계산 기간은 1 주일 길이 기간 = 7 * 24 * 60 * 60 * 1000; scheduledexecutorservice service = executors.newscheduledthreadpool (10); // 이제 milliseconds 지연, job1 service.scheduleatfixedrate (테스트, 지연, 기간, TimeUnit.milliseconds)를 실행합니다. }}/** 출력 결과 : 현재 날짜 = 2 월 2 월 2 일 17:32:01 CST 2011 arearliest 날짜 = tue 2 월 8 일 16:38:10 CST 2011date = tue Feb 8 16:38:10 CST 2011, job1date = tue Feb 15 16:38:10 CST 2011, job1*/
위의 코드는 매주 화요일 16:38:10에 스케줄링 작업의 기능을 구현합니다. 핵심은 현재 시간을 기준으로 마지막 화요일에 16:38:10의 절대 시간을 계산 한 다음 현재 시간과의 시차를 매개 변수로 계산하여 ScheduleDexCeutor 함수를 호출하는 것입니다. 최신 시간을 계산하기 위해 java.util.calendar의 기능이 사용됩니다. 먼저, 캘린더의 디자인 아이디어를 설명해야합니다. 캘린더에는 날짜를 고유하게 식별하는 다음 조합이 있습니다.
인용하다
연도 + 달 + day_of_month
연도 + Month + Week_of_month + day_of_week
연도 + 달 + day_of_week_in_month + day_of_week
연도 + day_of_year
연도 + day_of_week + Week_of_year
위의 조합은 시간의 시간 + 분 + 2 초로 결합되어 전체 시간 표시가됩니다.
위의 데모는 마지막 조합 방법을 채택합니다. 입력은 day_of_week, Hour_of_day, Minute, Sec 계산 원칙은 입력 day_of_week에서 비교를 시작하는 것입니다. 현재 날짜의 day_of_week보다 적은 경우, Week_of_year를 더 늘려야합니다. 즉, 현재 날짜에 Week_of_year를 추가하고 이전 값을 덮어 씁니다. 그것이 현재 day_of_week와 같으면 Hour_of_day를 계속 비교하십시오. 현재 day_of_week보다 큰 경우 java.util.calenda의 calendar.set (field, value) 함수를 직접 호출하여 day_of_week, Hour_of_day, Minute, INPUT 값 등을 지정하십시오. 최신 실행 시간을 계산하기 위해 입력 요구 사항에 따라 다른 조합을 선택할 수 있습니다.
위의 방법을 사용하여 작업 일정을 구현하는 것은 매우 번거 롭습니다. 이러한 복잡한 스케줄링 문제를 해결하기 위해보다 완전한 작업 스케줄링 도구가 필요합니다. 다행히 오픈 소스 툴킷 쿼츠는 이와 관련하여 큰 기능을 보여주었습니다.
석영
OpenSymphony 오픈 소스 조직은 Job Scheduling 분야의 또 다른 오픈 소스 프로젝트이며, J2EE 및 J2SE 응용 프로그램과 결합하거나 단독으로 사용될 수 있습니다. 석영은 10, 수백 또는 심지어 수만 개의 일자리를 운영하는 간단하거나 복잡한 프로그램을 만드는 데 사용될 수 있습니다.
예를 살펴 보겠습니다.
패키지 com.test.quartz; import static org.quartz.datebuilder.newdate; import static org.quartz.jobbuilder.newjob; import static org.quartz.simpleschedulebuilder.simpleschedule; import static org.quartz.triggerbuilder.newtrigger; java.util.gregoriancalendar; import org.quartz.jobdetail; import org.quartz.scheduler; import org.quartz.trigger; import org.quartz.impl.stdscheduleraltory; import org.quartz.impl.calendar.annualcalendar (problic voidtz); args) {try {// 스케줄러 스케줄러 스케줄러 만들기 스케줄러 = stdschedulerfactory.getDefaultScheduler (); // 트리거 트리거 트리거 (). withidentity ( "trigger1", "group1") // 이름/group () // scheduler가 추가되면 즉시 적용됩니다. 아직 멈추지 않을 때까지 돌진 .build (); // jobDetail jobDetail job = newJob (helloquartz.class) 정의 // 작업 클래스를 HelloQuartz 클래스로 정의합니다.이 클래스는 실제 실행 논리 ( "job1", "Group1") // 이름/group .usingjobdata ( "이름", "Quartz") // attribute.build ()를 정의합니다. //이 스케줄러에 추가합니다 .ScheduleJob (job, trigger); // START Scheduler.start (); // 닫힌 thread.sleep (10000); Scheduler.shutdown (True); } catch (예외 e) {e.printstacktrace (); }}} package com.test.quartz; import java.util.date; import org.quartz.disallowconcurrentexecution; import org.quartz.job; import org.quartz.jobdetail; import org.quartz.jobexecutioncontext; problic org.quartz.jobexecution excement; execute (jobExecutionContext Context) jobExecutionException {jobDetail alform = context.getJobDetail (); 문자열 이름 = detail.getJobdatamap (). getString ( "name"); System.out.println ( "" + name + "at" + new date ()에게 인사); }}위의 예를 통해 : 석영의 가장 중요한 3 가지 기본 요소 :
• 스케줄러 : 스케줄러. 모든 스케줄링은 그것에 의해 제어됩니다.
• 트리거 : 트리거 조건을 정의합니다. 이 예에서는 그 유형이 단순화기이며, 1 초마다 실행됩니다 (단순화는 아래에 자세히 설명되어 있습니다).
• JOBDETAIL & JOB : JOBDETAIL은 작업 데이터를 정의하고 실제 실행 로직이 작업 중입니다. 왜 JobDetail + 작업으로 설계되었으며 직접 작업을 사용하지 않습니까? 이는 작업을 동시에 실행할 수 있기 때문입니다. 스케줄러가 작업을 직접 사용하는 경우 동일한 작업 인스턴스에 동시에 액세스 해야하는 문제가 발생합니다. JobDetail & Job Method에서 셰들러가 실행할 때마다 JobDetail을 기반으로 새로운 작업 인스턴스를 생성하여 동시 액세스 문제를 피할 수 있습니다.
석영 API
Quartz의 API 스타일은 2.X 이후이며, 예제의 NewTrigger () 부분 인 DSL 스타일 (일반적으로 유창한 인터페이스 스타일을 의미)을 채택합니다. 빌더를 통해 구현됩니다. (다음 코드의 대부분은이 빌더를 나타냅니다)
// 작업 관련 Builderport 정적 org.quartz.jobbuilder.*; // 트리거 관련 Builderport 정적 org.quartz.triggerBuilder.*; import static org.quartz.simplesschedulebuilder.*; import static org.quartz.cronschedulebuilder.*; org.quartz.dailytimeintervalschedulebuilder.*; static org.quartz.calendarintervalschedulebuilder.*; // 날짜 관련 builderport static org.quartz.datebuilder.*; DSL 스타일은 더 일관성이 있고 쓰여지지 않을 것이며, 스타일을 사용하지 않기 때문에 더 쉽게 사용하지 않을 것입니다. 비교 : jobDetail jobDetail = new jobDetailImpl ( "jobDetail1", "Group1", helloquartz.class); jobDetail.getJobdatamap (). put ( "name", "Quartz"); simpleptriggerimpl trigger = new SimpletriggerImpl ( "trigger1", ""; trigger.set StartTime (news) 날짜 ()); trigger.setRepeatInterVal (1); trigger.setRepeatCount (-1);
이름과 그룹에 대해
JobDetail 및 Trigger에는 이름과 그룹이 있습니다.
이름은이 셰들러의 고유 식별자입니다. JobDetail 정의를 업데이트하려면 동일한 이름의 JobDetail 인스턴스를 설정하면됩니다.
Group은 조직 단위이며 Sheduler는 Scheduler.resumeJobs ()와 같은 전체 작업 그룹에 대해 일부 API를 제공합니다.
방아쇠
각 트리거를 자세히 설명하기 전에 트리거의 공통점을 이해해야합니다.
STARTTIME & ENDTIME
STARTTIME 및 ENDTIME에 의해 지정된 시간 간격이 트리거됩니다. 이 간격 외에 트리거는 트리거되지 않습니다. 모든 트리거에는이 두 속성이 포함됩니다.
우선 사항
스케줄러가 바쁘면 여러 트리거가 동시에 트리거 될 수 있지만 리소스가 불충분합니다 (예 : 실 풀이 불충분). 그런 다음 현재 가위 석재보다 더 나은 방법은 우선 순위를 설정하는 것입니다. 우선 순위를 먼저 실행하십시오. 한 방아쇠가 9:00이고 다른 트리거가 9:30 인 경우 우선 순위는 동시에 실행 된 트리거간에 만 작동합니다. 다음 우선 순위가 아무리 높더라도 이전의 우선 순위가 먼저 실행됩니다. 우선 순위의 기본값은 5이고 기본값은 음수 일 때 사용됩니다. 최대 값은 지정되지 않은 것으로 보이지만 Java 표준을 따르고 1-10을 사용하는 것이 좋습니다. 그렇지 않으면, [우선 순위는 10]을 볼 때 더 큰 값이 있는지 알고 있다면.
오해 (미스 트리거) 전략
유사한 스케줄러 리소스가 불충분하거나 기계가 충돌하고 다시 시작될 때, 트리거해야 할 당시, 즉 Fire 미스 미스 트리거가 트리거되지 않을 수 있습니다. 현재 Trigger는 이러한 상황을 처리하기위한 전략이 필요합니다. 각 트리거에 대한 선택적 전략은 다양합니다. 다음은 다음과 같이주의를 기울여야합니다.
오해 트리거는 임계 값을 가지며,이 값은 Jobstore에서 구성됩니다. Ramjobstore 이상은 org.quartz.jobstore.misfirethreshold입니다. 오해는이 임계 값을 초과하는 것으로만 계산됩니다. 이 임계 값보다 적어도 석영은 재고됩니다. 모든 오해 전략은 실제로 두 가지 질문에 대답합니다.
• 잘못된 트리거가 필요합니까?
• 오해가 발생하면 기존 스케줄링 시간을 조정 하시겠습니까?
예를 들어, Simpletrigger의 잘못된 불꽃 전략에는 다음이 포함됩니다.
• misfire_instruction_ignore_misfire_policy 이것은 누락 된 방아쇠를 무시하는 것이 아니라 오히려 잘못된 정책을 무시하는 것을 의미합니다. 리소스가 적절하고 기존 스케줄링 시간에 영향을 미치지 않을 때는 모든 오해 작업을 검색합니다. 예를 들어, Simpletrigger는 15 초마다 실행되며 중간에 5 분이 걸리고 20 가지 누락이 누락됩니다. 5 분 후에 자원이 충분하고 작업이 동시성을 허용한다고 가정하면 한 번에 트리거됩니다. 이 속성은 모든 트리거에게 적용됩니다.
• misfire_instruction_fire_now는 잘못된 작업을 무시하고 즉시 일정을 수행합니다. 일반적으로 한 번만 수행되는 작업에만 적용됩니다.
• misfire_instruction_reschedule_now_with_existing_repeat_count startTime을 현재 시간으로 설정하고 즉시 잘못된 작업을 변경합니다.
• misfire_instruction_reschedule_now_with_remaining_repeat_count misfireinstructionReschedulenow와 유사하게 repeat_count와 비슷한 차이점은 이미 잘못된 작업이 무시 될 것입니다.
• mistifire_instruction_reschedule_next_with_existing_count 다음 예정된 시간에 오해를 포함하여 디스패치 작업을 다시 시작하십시오.
• misfire_instruction_reschedule_next_with_remaining_count misfireintructionReschedulenext와 유사하게 차이는 이미 잘못된 작업이 무시 될 것입니다.
• misfire_instruction_smart_policy 모든 트리거의 불이익 기본값은 이는 "스마트 쿼츠에 처리 로직을 남겨두기 위해"를 의미합니다. 기본 전략은입니다.
• 일정이 한 번만 실행되면 misfire_instruction_fire_now를 사용하십시오.
• 무한한 스케줄링 인 경우 (RetureCount는 무한) Mistife_instruction_reschedule_next_with_remaining_count를 사용하십시오.
• 그렇지 않으면, misfire_instruction_reschedule_now_with_existing_repeat_count misfire를 사용하는 것은 상당히 복잡합니다.이 기사를 참조 할 수 있습니다.
달력
여기의 캘린더는 날짜를 계산하는 것이 아니라 jdk의 java.util.calendar가 아닙니다. 그 기능은 방아쇠의 시간을 보완하는 것입니다. 특정 특정 지점은 제외되거나 추가 될 수 있습니다.
"매월 25 일 0시에 카드 부채의 자동 상환"을 예로 들어, 우리는 매년 2 월 25 일의 시점을 배제하고자합니다 (2.14, 2 월이 확실히 파산 될 것입니다). 이번에는 달력으로 달성 할 수 있습니다.
예:
AnnualCalendar Cal = 새로운 연례 Calendar (); // 매년 정밀한 날로 실행되는 캘린더 정의, 즉 2.25 Java.util.calendar Excludeday = New GregorianCalendar (); ExcludedAy.setTime (NewDate (). In Monthonday (2, 25); Cal.setDayscluded (Excluded); // 제외 날짜 설정 날짜 2.25 Scheduler.addCalendar ( "Febcal", cal, false, false); // 스케줄러 가이 캘린더에 가입하십시오 // 트리거 트리거 트리거 = newtrigger (). withidentity ( "trigger1", "group1") .StartNow () // Scheduler가 추가되면 즉시 적용됩니다. .withSchedule (simpleschedule () .withinterValinseconds (1) .repeatforever ()) .build ();
석영은 다음 달력을 상당히 제공합니다. 모든 캘린더는 다음에 따라 제외되거나 포함 될 수 있습니다.
• Holidaycalendar. 20140613과 같은 특정 날짜를 지정하십시오. 하늘의 정확도.
• DailyCalendar. 매일의 기간 (rangestartingtime, RangeendingTime)을 지정하면 형식은 hh : mm [: ss [: mmmm]]입니다. 즉, 최대 정확도는 밀리 초에 도달 할 수 있습니다.
• 주간 칼렌 다. 매주 요일을 지정하면 선택적 값은 java.util.calendar.sunday입니다. 정확도는 날입니다.
• 월간 칼렌 다. 매월의 날을 지정하십시오. 선택적 값은 1-31입니다. 정확도는 날입니다
• 연례 칼렌 다. 일년 중 어느 날을 지정하십시오. 사용 방법은 위의 예에 표시된 것과 같습니다. 정확도는 날입니다.
• 크론 칼렌 다. Cron 표현식을 지정합니다. 정확도는 CRON 표현식, 즉 최대 정확도가 초에 도달 할 수 있습니다.
트리거 구현 클래스
Quartz에는 다음과 같은 트리거 구현이 있습니다.
단순한
특정 시간에 시작하여 특정 시간 간격 (밀리 초)에 수행되는 작업을 지정합니다. 다음과 비슷한 작업에 적합합니다. 9:00에 시작하여 1 시간마다 실행하십시오. 그 속성은 다음과 같습니다.
• 반복 반복 반복 간격
• 반복 수의 반복을 반복합니다. 실제 실행 수는 반복+1입니다. 시작 시간에 한 번 실행되기 때문입니다. 아래의 반복적 인 속성에도 동일하게 적용됩니다.
예:
CalendarIntervaltrigger
SimpleTrigger와 유사하게 특정 시간에 시작하여 특정 시간 간격으로 실행되는 작업을 지정합니다. 그러나 차이점은 Simpletrigger에 의해 지정된 시간 간격이 밀리 초이며, 매월 매월 실행되도록 지정할 방법이 없으며 (월간 간격은 고정 값이 아님) CalendarIntervaltrigger에서 지원하는 간격 단위에는 초, 분, 몇 월, 달, 몇 주 및 주가 포함됩니다. SimpleTrigger와 비교할 때 두 가지 장점이 있습니다. 1. 더 편리합니다. 예를 들어, 1 시간마다 실행하는 경우 1 시간 정도의 밀리 초의 수를 계산할 필요가 없습니다. 2. 몇 달과 몇 년 간격과 같이 고정 길이가 아닌 간격을 지원합니다. 그러나 단점은 정확도가 몇 초 밖에 도달 할 수 있다는 것입니다. 적절한 작업은 다음과 비슷합니다. 9:00에 시작하여 매주 9시에 한 번 실행하십시오. 그 속성은 다음과 같습니다.
• 간격 실행 간격
• 실행 간격 간격 간격 (초, 몇 분, 시간, 일, 몇 달, 몇 주, 주)
예:
CalendarIntervalIndays (1) // 하루에 한 번 실행합니다.
DailyTimeIntervaltrigger
작업이 매일 특정 기간 동안 특정 간격으로 수행되도록 지정하십시오. 지정된 주를 지원할 수 있습니다. 적절한 작업은 다음과 비슷합니다. 매일 9:00 ~ 18:00을 지정하고 70 초마다 실행되며 월요일부터 금요일까지만 실행됩니다. 그 속성은 다음과 같습니다.
• 매일 시작 시간 시작 시간
• 하루 종일 종료 시간
• 일주일 내내 처형 될 주
• 간격 실행 간격
• 실행 간격 간격 간격 (초, 몇 분, 시간, 일, 몇 달, 몇 주, 주)
• 반복 수의 반복을 반복합니다
예:
DailyTimeIntervalsCheedule () .StartingDailyAt (TimeOfday.HourAndMinuteofday (9, 0)) // 하루에 9:00에서 시작합니다. Intervalinhours (1) // 1 시간마다 실행됩니다. withRepeatCount (100) // 최대 100 회 반복 (실제로 100+1 회 실행) .Build (); DailingTimeInterValsChedule () .StartingDailyAt (TimeOfday.HourAndMinuteofday (9, 0)) // 하루에 9:00에 시작합니다. 이 방법은 실제로 시작 시간+간격*count .OndaysofTheweek (월요일, 화요일, 수요일, 목요일, 금요일)을 기준으로 EndTimeofday를 계산합니다. // 월요일부터 금요일까지 실행합니다.
크론 트리거
보다 복잡한 작업에 적합한 Linux Cron에 입력 한 구문을 지원합니다 (더 강력합니다). 기본적으로 위의 세 가지 트리거 중 대부분 (전부는 아님)을 다루고 있습니다. 물론 이해하기가 더 어렵습니다. 적절한 작업은 다음과 비슷합니다. 각각 0:00, 9:00 및 18:00에 매일 한 번 수행됩니다. 그 속성은 다음과 같습니다.
크론 표현
그러나이 표현 자체는 충분히 복잡합니다. 아래 지침이 있습니다. 예:
Cronschedule ( "0 0/2 8-17 * *?") // 매일 8 : 00-17 : 00에 2 분마다 실행합니다. * mon .build ();
크론 표현
| 위치 | 시간 영역 | 허용 값 | 특별한 가치 |
| 1 | 두번째 | 0-59 | , - * / |
| 2 | 분 | 0-59 | , - * / |
| 3 | 시간 | 0-23 | , - * / |
| 4 | 날짜 | 1-31 | , - *? / lwc |
| 5 | 월 | 1-12 | , - * / |
| 6 | 주 | 1-7 | , - *? / LC # |
| 7 | 연도 (선택 사항) | 1-31 | , - * / |
• Asterisk () : 모든 필드에서 해당 시간 영역에서 각 모멘트를 나타내는 데 사용될 수 있습니다. 예를 들어 분 필드에서는 "분당"을 의미합니다.
• 물음표 (?) :이 문자는 날짜와 주간 필드에만 사용되며 일반적으로 도트 문자와 동등한 "의미없는 가치"로 지정됩니다.
• 마이너스 부호 (-) : 범위를 표현합니다. 시간 필드에서 "10-12"가 사용되는 경우, 10 점에서 12 점, 즉 10,11,12;
• 쉼표 (,) : 목록 값을 표현합니다. "Mon, Wed, Fri"가 주간 필드에서 사용되는 경우 월요일, 수요일 및 금요일을 의미합니다.
• 슬래시 (/) : x/y는 동일 단계 시퀀스, x는 시작 값이고 y는 증분 단계 값입니다. 분 필드에서 0/15를 사용하는 경우 0, 15, 30 및 45 초로 표현되며 5/15는 분 필드에서 5, 20, 35, 50을 의미합니다. */y는 0/y와 동일합니다.
• L :이 캐릭터는 날짜 및 주간 필드에서만 사용되며 "마지막"의 의미를 나타내지 만 두 분야에서 다르게 의미합니다. l 날짜 필드의 L은 1 월 31 일 및 2 월 28 일과 같은 달의 마지막 날을 나타냅니다. L이 주에 사용된다면 토요일은 7에 해당합니다. 그러나 L가 주간 필드에 나타나고 값 X가 선행되면 "월의 마지막 X 일"을 의미합니다. 예를 들어, 6L은 월의 마지막 금요일을 의미합니다.
• W :이 캐릭터는 날짜 필드에만 나타날 수 있으며 선행 날짜를 수정하여 날짜에 가장 가까운 근무일을 나타냅니다. 예를 들어, 15W는 이달 15 일까지 가장 가까운 근무일을 나타냅니다. 매월 15 일이 토요일이면 14 일 금요일과 일치합니다. 매월 15 일이 일요일이라면 16 일 월요일과 일치합니다. 매월 15 일이 화요일이면 화요일은 15 일입니다. 그러나 관련된 일치 날짜는 그 달에 넘을 수 없다는 점에 유의해야합니다. 1W를 지정하는 경우 첫날이 토요일 인 경우 결과는 지난 달의 마지막 날이 아닌 월요일 3과 일치합니다. W 문자열은 단일 날짜 만 지정할 수 있지만 날짜 범위를 지정할 수는 없습니다.
• LW 조합 : LW는 날짜 필드에서 사용할 수 있으며 이는 월의 마지막 근무일을 의미합니다. 파운드 사인 (#) :이 캐릭터는 이번 주 근무일을 나타내는 주 필드에서만 사용할 수 있습니다. 예를 들어, 6#3은 달의 세 번째 금요일을 나타냅니다 (6은 금요일,#3은 현재 세 번째를 나타냅니다), 4#5는 이달의 다섯 번째 수요일을 나타냅니다.
• C :이 문자는 날짜 및 주간 필드에서만 사용되며 "캘린더"의 의미를 나타냅니다. 이는 계획과 관련된 날짜를 의미하며 날짜가 연관되지 않은 경우 캘린더의 모든 날짜와 동일합니다. 예를 들어, 날짜 필드의 5C는 달력 5 일 다음 날과 동일합니다. 1C는 주일의 일요일 이후의 첫날과 동일합니다.
CRON 표현은 특수 문자의 경우에 민감하지 않으며 이번 주 영어 사건의 약어에 민감하지 않습니다. 몇 가지 예 :
| 표현 | 설명 |
| 0 0 12 * *? | 매일 12시에 달리십시오 |
| 0 15 10? * * * | 매일 10:15에 실행하십시오 |
| 0 15 10 * *? | 매일 10:15에 실행하십시오 |
| 0 15 10 * *? * | 매일 10:15에 실행하십시오 |
| 0 15 10 * *? 2008 | 2008 년 하루 10:15에 달리십시오 |
| 0 * 14 * *? | 14:00 사이에 매 순간 달리며 매일 14:59에 끝납니다. |
| 0/5 14 * *? | 14:55에서 시작하여 14:55에 끝나는 매일 14:00에서 15:00까지 5 분마다 달리십시오. |
| 0/5 14,18 * *? | 매일 14:00에서 15:00까지 5 분마다 실행되며 매일 18:00에서 19:00까지 5 분마다 실행됩니다. |
| 0 0-5 14 * *? | 매일 14:00에서 14:05까지 매 순간을 달리십시오. |
| 0 10,44 14? 3 수 | 매주 수요일 14:10에서 14:44까지 1 분에 한 번 달리십시오. |
| 0 15 10? * Mon-Fri | 매주 월요일, 화요일, 수요일, 목요일, 목요일 및 금요일 10:15에 운영됩니다. |
| 0 15 10 15 * ? | 每月15日10:15分运行。 |
| 0 15 10 L * ? | 每月最后一天10:15分运行。 |
| 0 15 10 ? * 6L | 每月最后一个星期五10:15分运行。 |
| 0 15 10 ? * 6L 2007-2009 | 在2007,2008,2009年每个月的最后一个星期五的10:15分运行。 |
| 0 15 10 ? * 6#3 | 每月第三个星期五的10:15分运行。 |
JobDetail & Job
JobDetail是任务的定义,而Job是任务的执行逻辑。在JobDetail里会引用一个Job Class定义。一个最简单的例子:
public class JobTest { public static void main(String[] args) throws SchedulerException, IOException { JobDetail job=newJob() .ofType(DoNothingJob.class) //Check Job Class .withIdentity("job1", "group1") //Set name/group .withDescription("this is a test job") //Set description.usingJobData("age", 18) //Add attributes to ageJobDataMap .build(); job.getJobDataMap().put("name", "quertz"); //Add attribute name to JobDataMap //Define a SimpleTrigger trigger that executes once per second. Trigger trigger=newTrigger().startNow().withIdentity("trigger1") .withSchedule(simpleSchedule().withIntervalInSeconds(1) .repeatForever()) .build(); Scheduler sche=StdSchedulerFactory.getDefaultScheduler(); sche.scheduleJob(job, trigger); sche.start(); System.in.read(); sche.shutdown(); }}public class DoNothingJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("do nothing"); }}从上例我们可以看出,要定义一个任务,需要干几件事:
•创建一个org.quartz.Job的实现类,并实现实现自己的业务逻辑。比如上面的DoNothingJob。
•定义一个JobDetail,引用这个实现类
•加入scheduleJob Quartz调度一次任务,会干如下的事:
•JobClass jobClass=JobDetail.getJobClass()
•Job jobInstance=jobClass.newInstance()。所以Job实现类,必须有一个public的无参构建方法。
•jobInstance.execute(JobExecutionContext context)。JobExecutionContext是Job运行的上下文,可以获得Trigger、Scheduler、JobDetail的信息。
也就是说,每次调度都会创建一个新的Job实例,这样的好处是有些任务并发执行的时候,不存在对临界资源的访问问题――当然,如果需要共享JobDataMap的时候,还是存在临界资源的并发访问的问题。
JobDataMap
Job是newInstance的实例,那我怎么传值给它? 比如我现在有两个发送邮件的任务,一个是发给"liLei",一个发给"hanmeimei",不能说我要写两个Job实现类LiLeiSendEmailJob和HanMeiMeiSendEmailJob。实现的办法是通过JobDataMap。
每一个JobDetail都会有一个JobDataMap。JobDataMap本质就是一个Map的扩展类,只是提供了一些更便捷的方法,比如getString()之类的。
我们可以在定义JobDetail,加入属性值,方式有二:
•newJob().usingJobData("age", 18) //加入属性到ageJobDataMap
•job.getJobDataMap().put("name", "quertz"); //加入属性name到JobDataMap
然后在Job中可以获取这个JobDataMap的值,方式同样有二:
public class HelloQuartz implements Job { private String name; public void execute(JobExecutionContext context) throws JobExecutionException { JobDetail detail = context.getJobDetail(); JobDataMap map = detail.getJobDataMap(); //Method 1: Obtain JobDataMap System.out.println("say hello to " + name + "[" + map.getInt("age") + "]" + " at " + new Date()); } //Method 2: The setter method of the property will automatically inject the JobDataMap attribute into public void setName(String name) { this.name = name; }}对于同一个JobDetail实例,执行的多个Job实例,是共享同样的JobDataMap,也就是说,如果你在任务里修改了里面的值,会对其他Job实例(并发的或者后续的)造成影响。
除了JobDetail,Trigger同样有一个JobDataMap,共享范围是所有使用这个Trigger的Job实例。
Job并发
Job是有可能并发执行的,比如一个任务要执行10秒中,而调度算法是每秒中触发1次,那么就有可能多个任务被并发执行。
有时候我们并不想任务并发执行,比如这个任务要去”获得数据库中所有未发送邮件的名单“,如果是并发执行,就需要一个数据库锁去避免一个数据被多次处理。这个时候一个@DisallowConcurrentExecution解决这个问题。就是这样:
public class DoNothingJob implements Job { @DisallowConcurrentExecution public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("do nothing"); }}注意,@DisallowConcurrentExecution是对JobDetail实例生效,也就是如果你定义两个JobDetail,引用同一个Job类,是可以并发执行的。
JobExecutionException
Job.execute()方法是不允许抛出除JobExecutionException之外的所有异常的(包括RuntimeException),所以编码的时候,最好是try-catch住所有的Throwable,小心处理。
其他属性
•Durability(耐久性?) 如果一个任务不是durable,那么当没有Trigger关联它的时候,它就会被自动删除。
•RequestsRecovery 如果一个任务是"requests recovery",那么当任务运行过程非正常退出时(比如进程崩溃,机器断电,但不包括抛出异常这种情况),Quartz再次启动时,会重新运行一次这个任务实例。
可以通过JobExecutionContext.isRecovering()查询任务是否是被恢复的。
스케줄러
•Scheduler就是Quartz的大脑,所有任务都是由它来设施。
•Schduelr包含一个两个重要组件: JobStore和ThreadPool。
•JobStore是会来存储运行时信息的,包括Trigger,Schduler,JobDetail,业务锁等。它有多种实现RAMJob(内存实现),JobStoreTX(JDBC,事务由Quartz管理),JobStoreCMT(JDBC,使用容器事务),ClusteredJobStore(集群实现)、TerracottaJobStore(什么是Terractta)。
•ThreadPool就是线程池,Quartz有自己的线程池实现。所有任务的都会由线程池执行。
SchedulerFactory
SchdulerFactory,顾名思义就是来用创建Schduler了,有两个实现:DirectSchedulerFactory和StdSchdulerFactory。前者可以用来在代码里定制你自己的Schduler参数。后者是直接读取classpath下的quartz.properties(不存在就都使用默认值)配置来实例化Schduler。通常来讲,我们使用StdSchdulerFactory也就足够了。
SchdulerFactory本身是支持创建RMI stub的,可以用来管理远程的Scheduler,功能与本地一样,可以远程提交个Job什么的。DirectSchedulerFactory的创建接口:
/** * Same as * {@link DirectSchedulerFactory#createScheduler(ThreadPool threadPool, JobStore jobStore)}, * with the addition of specifying the scheduler name and instance ID. This * scheduler can only be retrieved via * {@link DirectSchedulerFactory#getScheduler(String)} * * @param schedulerName * The name for the scheduler. * @param schedulerInstanceId * The instance ID for the scheduler. * @param threadPool * The thread pool for executing jobs * @param jobStore * The type of job store * @throws SchedulerException * if initialization failed */ public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore) throws SchedulerException;StdSchdulerFactory的配置例子, 更多配置,参考Quartz配置指南:
org.quartz.scheduler.instanceName = DefaultQuartzSchedulerorg.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount = 10 org.quartz.threadPool.threadPriority = 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = trueorg.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
三、Quartz 集成Spring
开发一个job类,普通java类,需要有一个执行的方法:
package com.tgb.lk.demo.quartz;import java.util.Date;public class MyJob { public void work() { System.out.println("date:" + new Date().toString()); }}把类放到spring容器中,可以使用配置也可以使用注解:
<bean id="myJob" />
配置jobDetail,指定job对象:
<!-- 配置jobDetail,指定job对象--> <bean id="accountJobDetail"> <property name="targetObject"> <ref bean="accountJob" /> </property> <property name="targetMethod"> <value>work</value> </property> </bean>
配置一个trigger,需要指定一个cron表达式,指定任务的执行时机:
<!-- accountTrigger 的配置--> <bean id="accountTrigger" > <property name="jobDetail"> <ref bean="accountJobDetail" /> </property> <property name="cronExpression"> <value>0/3 * * * * ?</value> </property> </bean>
配置调度工厂:
<!-- 启动触发器的配置开始--> <bean name="startQuertz" lazy-init="false" autowire="no" > <property name="triggers"> <list> <ref bean="myJobTrigger" /> </list> </property> </bean> <!-- 启动触发器的配置结束-->
项目启动,定时器开始执行。
四、分析不同定时任务优缺点,寻找一种符合你项目需求的定时任务Timer管理延时任务的缺陷
以前在项目中也经常使用定时器,比如每隔一段时间清理项目中的一些垃圾文件,每隔一段时间进行日志清理;然而Timer是存在一些缺陷的,因为Timer在执行定时任务时只会创建一个线程,所以如果存在多个任务,且任务时间过长,超过了两个任务的间隔时间,会发生一些缺陷
Timer当任务抛出异常时的缺陷
如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行
Timer执行周期任务时依赖系统时间
Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化,ScheduledExecutorService基于时间的延迟,不会由于系统时间的改变发生执行变化。
对异常的处理
Quartz的某次执行任务过程中抛出异常,不影响下一次任务的执行,当下一次执行时间到来时,定时器会再次执行任务;而TimerTask则不同,一旦某个任务在执行过程中抛出异常,则整个定时器生命周期就结束,以后永远不会再执行定时器任务。
精确到和功能
Quartz每次执行任务都创建一个新的任务类对象,而TimerTask则每次使用同一个任务类对象。 Quartz可以通过cron表达式精确到特定时间执行,而TimerTask不能。Quartz拥有TimerTask所有的功能,而TimerTask则没有上述,基本说明了在以后的开发中尽可能使用ScheduledExecutorService(JDK1.5以后)替代Timer。
五、cron 在线表达式生成器http://cron.qqe2.com/附录cron 表达式
cron表达式用于配置cronTrigger的实例。cron表达式实际上是由七个子表达式组成。这些表达式之间用空格分隔。
•Seconds (秒)
•Minutes(分)
•Hours(小时)
•Day-of-Month (天)
•Month(月)
•Day-of-Week (周)
•Year(年)
例:"0 0 12 ? * WED” 意思是:每个星期三的中午12点执行。个别子表达式可以包含范围或者列表。例如:上面例子中的WED可以换成"MON-FRI","MON,WED,FRI",甚至"MON-WED,SAT"。子表达式范围:
•Seconds (0~59)
•Minutes (0~59)
•Hours (0~23)
•Day-of-Month (1~31,但是要注意有些月份没有31天)
•Month (0~11,或者"JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV,DEC")
•Day-of-Week (1~7,1=SUN 或者"SUN, MON, TUE, WED, THU, FRI, SAT”)
•Year (1970~2099)
Cron表达式的格式:秒分时日月周年(可选)。
字段名| 允许的值| 允许的特殊字符------- | ------ | ------ | ------ 秒| 0-59 | , - * / 分| 0-59 | , - * / 小时| 0-23 | , - * / 日| 1-31 | , - * ? / LWC 月| 1-12 or JAN-DEC | , - * / 周几| 1-7 or SUN-SAT | , - * ? / LC # 年(可选字段) | empty 1970-2099 | , - * /
字符含义:
•*:代表所有可能的值。因此,“*”在Month中表示每个月,在Day-of-Month中表示每天,在Hours表示每小时
•-:表示指定范围。
•,:表示列出枚举值。例如:在Minutes子表达式中,“5,20”表示在5分钟和20分钟触发。
•/:被用于指定增量。例如:在Minutes子表达式中,“0/15”表示从0分钟开始,每15分钟执行一次。"3/20"表示从第三分钟开始,每20分钟执行一次。和"3,23,43"(表示第3,23,43分钟触发)的含义一样。
•?:用在Day-of-Month和Day-of-Week中,指“没有具体的值”。当两个子表达式其中一个被指定了值以后,为了避免冲突,需要将另外一个的值设为“?”。例如:想在每月20日触发调度,不管20号是星期几,只能用如下写法:0 0 0 20 * ?,其中最后以为只能用“?”,而不能用“*”。
•L:用在day-of-month和day-of-week字串中。它是单词“last”的缩写。它在两个子表达式中的含义是不同的。
•在day-of-month中,“L”表示一个月的最后一天,一月31号,3月30号。
•在day-of-week中,“L”表示一个星期的最后一天,也就是“7”或者“SAT”
•如果“L”前有具体内容,它就有其他的含义了。例如:“6L”表示这个月的倒数第六天。“FRIL”表示这个月的最后一个星期五。
•注意:在使用“L”参数时,不要指定列表或者范围,这样会出现问题。
•W:“Weekday”的缩写。只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第16天即周一触发;如果这个月第15天是周二,那么就在触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在day-of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日,即最后一个星期五。
•# :只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用"6#3" or "FRI#3"指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。
表达式例子:
0 * * * * ? 每1分钟触发一次
0 0 * * * ? 每天每1小时触发一次
0 0 10 * * ? 每天10点触发一次
0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
0 30 9 1 * ? 每月1号上午9点半
0 15 10 15 * ? 每月15日上午10:15触发
*/5 * * * * ? 每隔5秒执行一次
0 */1 * * * ? 每隔1分钟执行一次
0 0 5-15 * * ? 每天5-15点整点触发
0 0/3 * * * ? 每三分钟触发一次
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0 12 ? * WED 表示每个星期三中午12点
0 0 17 ? * TUES,THUR,SAT 每周二、四、六下午五点
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
0 0 23 L * ? 每月最后一天23点执行一次
0 15 10 L * ? 每月最后一日的上午10:15触发
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 * * ? 2005 2005年的每天上午10:15触发
0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
The above timed tasks (example explanation) in Java implementation web applications are all the content I share with you. 나는 당신이 당신에게 참조를 줄 수 있기를 바랍니다. 그리고 당신이 wulin.com을 더 지원할 수 있기를 바랍니다.