Une tâche de synchronisation est fonction de spécifier une plage de temps future pour effectuer certaines tâches. Dans les applications Web actuelles, la plupart des applications ont des fonctions de planification des tâches. Ils ont leur propre syntaxe et solutions pour différentes voix et systèmes d'exploitation. Le système d'exploitation Windows appelle la planification des tâches informatiques et les services CRON de Linux fournissent cette fonction. Cette fonction est souvent impliquée dans notre système de développement commercial. Ce chat utilisera la langue Java pour effectuer l'utilisation de tâches de synchronisation couramment utilisées dans les travaux de développement quotidien, dans l'espoir d'aider le travail et l'étude de chacun.
1. Scénarios de tâche chronométrés
(1) le flux de travail de traitement de conduite
En tant que nouvelle commande prépayée, elle est initialisée et placée. Si la commande n'est pas payée dans le délai spécifié, il sera considéré que la commande de délai de temps sera fermée. Il est largement utilisé dans le système de commerce électronique. Les utilisateurs achètent des marchandises et génèrent des commandes mais ne font pas de paiements. Si la commande n'est pas payée dans les 30 minutes, la commande sera fermée (et le nombre de personnes rencontrant ce scénario est énorme), et il est impossible d'utiliser une intervention manuelle.
(2) Maintenance du système
Les travaux de répartition obtiendront le journal d'exception du système et stockeront certaines données de points clés dans la base de données. Il sera jeté dans la base de données à 23h30 tous les jours de la semaine (à l'exception des vacances et des jours de semaine), et générera un fichier XML et l'envoyez à l'adresse e-mail d'un certain employé.
(3) Fournir des services de rappel dans la demande.
Le système rappelle régulièrement à l'utilisateur connecté d'effectuer des travaux connexes à un certain moment.
(4) les tâches de réconciliation chronométrées
La société et les sociétés à trois partis (opérateurs, banques, etc.) effectueront la réconciliation de l'entreprise le même jour après minuit tous les jours, enverront les données des résultats des informations de réconciliation à l'adresse e-mail de la personne concernée en charge et traitent les données de décalage pendant les heures de travail du lendemain.
(5) Statistiques de données
Il existe de nombreux enregistrements de données, et la lecture et l'interrogation en temps réel à partir de la base de données généreront un certain temps, qui concerne l'expérience client et les besoins de performance. Par conséquent, les données sont résumées chaque semaine (jours, heures), afin que les données puissent être rapidement présentées lors de l'affichage des données.
Il existe de nombreux scénarios où les tâches de synchronisation sont utilisées ... il semble que les tâches de synchronisation soient vraiment largement utilisées dans notre développement quotidien ...
2. Explication des tâches de synchronisation traditionnelles temporaires
Je crois que tout le monde est déjà très familier avec Java.util.timer. C'est le moyen le plus simple de mettre en œuvre la planification des tâches. Voici un exemple spécifique:
Package com.ibm.scheduler; import java.util.timer; import java.util.timertask; classe publique TimerTest étend Timemertask {private String jobname = ""; public tiperSTest (String JobName) {super (); this.jobname = jobname; } @Override public void run () {System.out.println ("exécuter" + jobname); } public static void main (String [] args) {Timer Timer = new Timer (); Long Delay1 = 1 * 1000; longue période 1 = 1000; // Après 1 seconde à partir de maintenant, exécutez Job1 TIMER.SCHEDULE (new TimerTest ("Job1"), Delay1, période1); long retard2 = 2 * 1000; longue période 2 = 2000; // Après 2 secondes à partir de maintenant, exécutez Job2 Timer.Schedule (new TimerTest ("Job2"), Delay2, period2); }} / **
Résultat de sortie:
Exécuter Job1
Exécuter Job1
Exécuter Job2
Exécuter Job1
Exécuter Job1
Exécuter Job2
* /
Les classes de base qui utilisent la minuterie pour implémenter la planification des tâches sont la minuterie et la tricotage. La minuterie est responsable de la définition du temps d'exécution de démarrage et d'intervalle de Timertask. L'utilisateur n'a besoin que de créer une classe d'héritage de Timemertask, d'implémenter sa propre méthode d'exécution, puis de la jeter à la minuterie pour l'exécution. Le noyau de conception de la minuterie est une liste de tâches et un tâche. La minuterie jette les tâches reçues dans sa liste de tâches, qui trie la liste de tâche en fonction du temps d'exécution initial de la tâche. TimerThread commence à devenir un fil de démon lors de la création de la minuterie. Ce fil interroge toutes les tâches, trouve une tâche récemment exécutée, puis dort. Lorsque l'heure de début de la plus récente à exécuter, TimerThread est éveillé et la tâche est exécutée. Après cela, TimerThread met à jour la tâche la plus récente à exécuter et continue d'hiberner.
L'avantage de la minuterie est qu'il est simple et facile à utiliser, mais comme toutes les tâches sont planifiées par le même thread, toutes les tâches sont exécutées en série et une seule tâche peut être exécutée en même temps. Le retard ou l'exception de la tâche précédente affectera les tâches suivantes (ce point doit être prêté attention).
ScheduleDexecutor
Compte tenu des lacunes ci-dessus de la minuterie, Java 5 a lancé ScheduleDexecutor basé sur la conception de pool de threads. L'idée de conception est que chaque tâche planifiée sera exécutée par un fil dans le pool de threads, de sorte que les tâches sont exécutées simultanément et ne seront pas dérangées les unes par les autres. Il convient de noter que ScheduledExEcutor ne démarrera vraiment un thread que lorsque le temps d'exécution de la tâche arrive, et le ScheduledExecutor interroge la tâche pour le reste du temps.
Package com.ibm.scheduler; Importer java.util.concurrent.executors; importer java.util.concurrent.schedulexecutorService; import java.util.concurrent.TimeUnit; classe publique ScheduledExecuTTOsttest implémente Runnable {private string jobname = ""; public ScheduleDexeCutOrest (String JobName) {super (); this.jobname = jobname; } @Override public void run () {System.out.println ("exécuter" + jobname); } public static void main (String [] args) {ScheduleDexeCutOrService Service = exécutors.newScheduledThreadPool (10); long initialdelay1 = 1; longue période 1 = 1; // Après 1 seconde à partir de maintenant, exécutez Job1 Service.ScheDuleAtFixeDrate (nouveau ScheduleDexeCutOrest ("Job1"), initialDelay1, period1, timeunit.seconds); long initialdelay2 = 1; long retard2 = 1; // Après 2 secondes à partir de maintenant, exécutez Job2 Service.ScheduleWithFixedDelay (nouveau ScheduleDexeCutOrest ("Job2"), initialDelay2, Delay2, timeUnit.seconds); }}/ ** Résultat de sortie: exécuter job1execute job1execute job2execute job1execute job1execute job2 * /
Le code ci-dessus affiche deux méthodes de planification les plus couramment utilisées, ScheduleAtFixeDrate et ScheduleWithFixedDelay, dans ScheduleDelta. ScheduleAtFixeDrate Chaque temps d'exécution est un intervalle de temps repoussant depuis le début de la tâche précédente, c'est-à-dire que chaque temps d'exécution est: InitialDelay, InitialDelay + Période, InitialDelay + 2 * Période,… ScheduleWithFixedDelay InitialDelay + 2 * Executetime + 2 * retard. On peut voir que ScheduleAtFixeDrate est basé sur des intervalles de temps fixes pour la planification des tâches, et ScheduleWithFixedDelay dépend de la durée de chaque temps d'exécution de la tâche et est basé sur des intervalles de temps non fixés pour la planification des tâches.
Utilisez ScheduleDexecutor et Calendar pour implémenter une planification de tâches complexe
Timer et ScheduleDexecutor ne peuvent fournir que la planification des tâches en fonction de l'heure de début et de l'intervalle de répétition, et ne sont pas compétents pour des exigences de planification plus complexes. Par exemple, définissez la tâche à exécuter tous les mardis à 16:38:10. Cette fonction ne peut pas être directement implémentée à l'aide de TIMER ou de ScheduleDexecutor, mais nous pouvons l'implémenter indirectement à l'aide du calendrier.
Package com.ibm.scheduler; import java.util.calendar; import java.util.date; importer java.util.timertask; import java.util.concurrent.execUtors; import java.util.concurrent.scheduledexecurservice; import java.util.concurrent.timenit cordulExpe2 étend Timertask {private string jobName = ""; public ScheduleDExCeUtOrest2 (String JobName) {super (); this.jobname = jobname; } @Override public void run () {System.out.println ("date =" + new Date () + ", exécuter" + jobname); } / ** * Calculez la dernière fois qui commence à partir de l'heure actuelle actuelle et remplit les conditions dayofweek, hourofday, * minuteofhour, secondofminite * @return * / calendrier public GetEarlistDate (calendrier actuel, int-dayofweek, int hourofday, int mistjous Hour_of_day, minute, deuxième, etc. int currentweekofyear = currentDate.get (calendar.week_of_year); int currentdayofweek = currentDate.get (calendar.day_of_week); int currentHour = currentDate.get (calendar.hour_of_day); int currentMinUte = currentDate.get (calendar.Minute); INT CURRENTESCOND = CurrentDate.get (Calendar.Second); // Si le jourofweek dans la condition d'entrée est inférieur à la semaine de la date de la date actuelle, la semaine_of_year doit être reportée d'une semaine booléenne weeklater = false; if (dayofweek <currentdayofweek) {weekLater = true; } else if (dayofweek == currentdayofweek) {// Lorsque la condition d'entrée est égale au jour de la week de la date actuelle, si // hourofday dans la condition d'entrée est inférieur à // courant de la date actuelle, week_of_year doit être reporté d'une semaine si (houroday <currenthourhour) {weeklater = true; } else if (hourofday == currenthour) {// Lorsque la condition d'entrée est égale au jour de laweek, hourofday de la date actuelle, // si la minute de la condition d'entrée est inférieure à // couramment actuel de la date actuelle, semaine_of_year doit être reportée par une semaine si (minuteofhour <actuelMinute) {weekLater = true; } else if (minuteofHour == CurrentSecond) {// Lorsque la condition d'entrée est égale au jourofweek, hourofday, // minuteofhour, if // secondofminite dans la condition d'entrée est inférieure à la courants de la date de la date actuelle, // alors semaine_of_year doit être postponé par une semaine si (secondefminite <CurrentSecond) {weekLater = true; }}}}} if (weekLater) {// Définir la semaine_of_year dans la date actuelle pour reporter une semaine CurrentDate.set (calendar.week_of_year, currentweekofyear + 1); } // Définir Day_of_week, hour_of_day, minute, deuxième dans la date actuelle comme valeurs en condition d'entrée. currentDate.set (calendar.day_of_week, dayofweek); currentDate.set (calendar.hour_of_day, hourofday); currentDate.set (calendar.minute, minuteofhour); currentDate.set (calendar.second, secondofminite); return currentDate; } public static void main (String [] args) lève une exception {ScheduleDExCeUtOrest2 test = new ScheduleNExCeUtOrest2 ("Job1"); // Obtenez le calendrier d'heure actuel CurrentDate = calendar.getInstance (); long currentDateLong = currentDate.getTime (). getTime (); System.out.println ("Current Date =" + CurrentDate.getTime (). ToString ()); // Calculez le dernier temps d'exécution qui répond à la condition Calendar ForetDate = TEST .GetArlistDate (CurrentDate, 3, 16, 38, 10); longtemps plus tôtDateLong = EarlyDate.getTime (). GetTime (); System.out.println ("Early Date =" + ForedDate.getTime (). ToString ()); // Calculez l'intervalle de temps de l'heure actuelle à la dernière heure d'exécution de retard = Présentement préalable - CurrentDatelong; // Calculer la période d'exécution dure une période d'une semaine d'une période de longueur = 7 * 24 * 60 * 60 * 1000; ScheduleDexeCutOrService Service = exécuteurs.NewScheduledThreadPool (10); // à partir de maintenant retarder les millisecondes, exécuter Job1 Service.ScheduleAtFixeDrate (test, retard, période, timeunit.milliseconds); }}/ ** Résultat de sortie: Date actuelle = mer février 02 17:32:01 CST 2011 Date la plus gagnante = Tue 8 février 16:38:10 CST 2011Date = Tue 8 février 16:38:10 CST 2011, EXECUTE JOB1DATE = TUE 15:3:38:10 CST 2011, EXECUTE JOB1 * /
Le code ci-dessus implémente la fonction des tâches de planification à 16:38:10 tous les mardis. Le noyau consiste à calculer l'heure absolue de 16:38:10 le dernier mardi en fonction de l'heure actuelle, puis de calculer la différence de temps par rapport à l'heure actuelle en tant que paramètre pour appeler la fonction ScheduleNExceutor. Pour calculer la dernière fois, la fonction de java.util.calendar est utilisée. Tout d'abord, nous devons expliquer certaines idées de conception du calendrier. Le calendrier a les combinaisons suivantes qui identifient uniquement une date:
Citation
Année + mois + day_of_month
Année + mois + semaine_of_month + day_of_week
Année + mois + day_of_week_in_month + day_of_week
Année + day_of_year
Année + day_of_week + semaine_of_year
Les combinaisons ci-dessus sont combinées avec Hourofday + Minute + Second pour être une marque complète.
La démo ci-dessus adopte la dernière méthode de combinaison. L'entrée est day_of_week, hour_of_day, minute, deuxième et la date actuelle, et la sortie est une date future qui satisfait day_of_week, hour_of_day, minute, deuxième et est la plus proche de la date en cours. Le principe du calcul est de démarrer la comparaison à partir de l'entrée day_of_week. S'il s'agit de moins que le jour_of_week de la date actuelle, vous devez augmenter davantage la semaine_of_year, c'est-à-dire ajouter la semaine_of_year dans la date en cours et écraser l'ancienne valeur; S'il est égal au jour actuel_of_week, continuez à comparer Hour_OF_DAY; S'il est supérieur à la fonction Day_of_of_of_, appelez directement la fonction Calendar.Set (champ, valeur) de java.util.calenda pour affecter le jour_of_week, hour_of_day, minute, seconde à la valeur d'entrée, et ainsi de suite jusqu'à ce que la comparaison soit atteinte à la seconde. Nous pouvons sélectionner différentes combinaisons en fonction des exigences d'entrée pour calculer le dernier temps d'exécution.
Il est assez lourd d'implémenter la planification des tâches en utilisant la méthode ci-dessus. Nous espérons avoir besoin d'un outil de planification de tâches plus complet pour résoudre ces problèmes de planification complexes. Heureusement, le quartz à outils open source a montré de grandes capacités à cet égard.
Quartz
Opensymphony Open Source Organisation est un autre projet open source dans le domaine de la planification des travaux, qui peut être combiné avec des applications J2EE et J2SE ou utilisé seuls. Le quartz peut être utilisé pour créer des programmes simples ou complexes qui exécutent dix, des centaines ou même des dizaines de milliers d'emplois.
Jetons un coup d'œil à un exemple:
package 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; importation static org.quartz.triggerbuilder.newtrigger; import java.util.gregoriancalendar; import org.quartz.jobdetail; import org.quartz.scheduler; import org.quartz.trigger; import org.quartz.impl.stdschedulerfactory; import org.quartz.impl.calendar. args) {try {// Créer un planificateur de planificateur Scheduler = stdschedulerfactory.getDefaultScheduler (); // Définir un déclencheur de déclenchement déclencheur = newtrigger (). Witality ("Trigger1", "Group1") // Définir le nom / groupe .startNow () // Une fois que le planificateur est ajouté, il faut immédiatement. se précipiter jusqu'à ce qu'il ne soit toujours pas arrêté.build (); // Définir un travail jobDtail JobDetail = newJob (helloquartz.class) // définir la classe de travail comme une classe Helloquartz, qui est la véritable exécution logique.withIdentity ("Job1", "Group1") // define name / groupe .usingJobData ("name", "quartz") //fine the attribt.build (); // Ajouter à ce planificateur.scheduleJob (travail, déclencheur); // Démarrer Scheduler.Start (); // Fermer File.Sleep (10000); Scheduler.shutdown (true); } catch (exception e) {e.printStackTrace (); }}} package com.test.quartz; importer java.util.date; import org.quartz.disallowconcurrentExecution; import org.quartz.job; import org.quartz.jobdetail; import org.quartz.jobexecuteContex EXECUTE (JobExEcutionContext Context) lève JobExEcutionException {jobDetail Detail = context.getJobDetail (); String name = Detail.getJobDatAmap (). GetString ("name"); System.out.println ("dire bonjour à" + name + "à" + new Date ()); }}À travers les exemples ci-dessus: les 3 éléments de base les plus importants du quartz:
• Planificateur: planificateur. Toute la planification est contrôlée par elle.
• Déclencheur: définit la condition de déclenchement. Dans l'exemple, son type est SimpleTrigger, qui est exécuté toutes les 1 secondes (ce qui est SimpleTrigger, sera décrit en détail ci-dessous).
• Jobdetail & Job: JobDetail définit les données de tâche, et la logique d'exécution réelle est dans le travail, dans l'exemple, helloquartz. Pourquoi est-il conçu comme un travail Jobdetail + et ne pas utiliser directement Job? En effet, il est possible d'exécuter simultanément des tâches. Si Scheduler utilise directement des travaux, il y aura un problème d'accès simultané à la même instance de travail. Dans la méthode Jobdetail & Job, chaque fois que le Sheduler s'exécute, il créera une nouvelle instance de travail basée sur le Jobdetail, afin que le problème de l'accès simultané puisse être évité.
API à quartz
Le style API de Quartz est après 2.x et adopte le style DSL (signifie généralement le style d'interface fluide), qui est la partie newtrigger () dans l'exemple. Il est mis en œuvre via Builder, qui est le suivant. (La plupart des codes suivants se réfèrent à ces constructeurs)
// Builderport lié à l'emploi Static org.quartz.jobbuilder. *; // déclencheur Builderport statique org.quartz.triggerbuilder. *; import static org.quartz.cronschedulebuduled org.quartz.dailyTimeIntervalscheduleBuilder. *; Importer static org.quartz.calendarintervalschedulebuilder. *; // date-lié à Builderport statique org.quartz.dateBuilder. *; Le style DSL sera plus cohérent pour comprendre et depuis ce n'est pas le style d'utilisation, il sera plus cohérent pour comprendre, et depuis ce n'est pas le style d'utilisation, il sera plus cohérent pour comprendre, et depuis ce n'est pas le style d'utilisation, il sera plus cohérent. Compare: jobDetail jobdetail = new JobDetailImpl ("jobdetail1", "group1", helloquartz.class); jobdetail.getJobdatamap (). Put ("name", "quartz"); SimpletGirgerImpl Trigger = new SimpleTriggerImpl (waD Date ()); Trigger.SetRepeatInterval (1); Trigger.SetRepeatCount (-1);Sur le nom et le groupe
Jobdetail et Trigger ont le nom et le groupe.
Le nom est leur identifiant unique dans ce Sheduler. Si nous souhaitons mettre à jour une définition de Jobdetail, nous avons juste besoin de définir une instance Jobdetail avec le même nom.
Group est une unité d'organisation, et Sheduler fournira quelques API à l'ensemble du groupe d'opérations, telles que Scheduler.ResumeJobs ().
Déclenchement
Avant de commencer à expliquer chaque déclencheur en détail, vous devez comprendre certaines points communs de déclencheur.
Démarrage et fin
L'intervalle de temps spécifié par starttime et fin sera déclenché. En dehors de cet intervalle, le déclencheur ne sera pas déclenché. Tous les déclencheurs contiendront ces deux propriétés.
Priorité
Lorsque le planificateur est occupé, plusieurs déclencheurs peuvent être déclenchés en même temps, mais les ressources sont insuffisantes (comme un pool de threads insuffisant). Ensuite, à ce moment, une meilleure façon qu'un tissu en pierre de ciseaux est de fixer la priorité. Exécutez d'abord la priorité. Il convient de noter que la priorité ne fonctionnera que entre les déclencheurs exécutés en même temps, si un déclencheur est 9h00 et l'autre déclencheur est 9h30. Alors, peu importe la hauteur de la prochaine priorité, la précédente sera exécutée en premier. La valeur par défaut de la priorité est de 5 et la valeur par défaut est utilisée lorsqu'elle est négative. La valeur maximale ne semble pas être spécifiée, mais il est recommandé de suivre la norme Java et d'utiliser 1-10. Sinon, si vous savez s'il y a une valeur plus grande lorsque vous voyez [la priorité est 10].
Stratégie de rafale de rafale (Miss Trigger)
Lorsque des ressources de planificateur similaires sont insuffisantes ou lorsque la machine se bloque et redémarre, il est possible que certains déclencheurs ne soient pas déclenchés au moment où ils doivent être déclenchés, c'est-à-dire Miss Fire. Pour le moment, Trigger a besoin d'une stratégie pour gérer cette situation. Les stratégies facultatives pour chaque déclencheur varient. Voici deux points pour prêter attention:
Mis Fire Trigger a une valeur de seuil, qui est configurée dans le travail. Plus que Ramjobstore est org.quartz.jobstore.MisfireThreshold. Les ratés ne seront comptés que comme dépassant ce seuil. Moins de ce seuil, le quartz sera reculé. Toutes les stratégies de ratés répondent en fait deux questions:
• Est-ce que cela a un raté à rediffuser?
• Si des ratés se produisent, voulez-vous ajuster l'heure de planification existante?
Par exemple, la stratégie de rafale de SimpleTrigger comprend:
• MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY Cela ne signifie pas ignorer le déclencheur manqué, mais plutôt ignorer la politique de ratés. Il récupérera toutes les tâches de ratés lorsque la ressource sera appropriée et n'affectera pas l'heure de planification existante. Par exemple, SimpleTrigger s'exécute toutes les 15 secondes, et il a 5 minutes au milieu et manque 20 manquants. Après 5 minutes, en supposant que les ressources sont suffisantes et que la tâche permet la concurrence, elle sera déclenchée en même temps. Cette propriété s'applique à tous les déclencheurs.
• Misfire_instruction_fire_now Ignorez des tâches qui ont été ratées et effectuent des horaires immédiatement. Cela ne s'applique généralement qu'aux tâches qui ne sont effectuées qu'une seule fois.
• Misfire_instruction_Reschedule_Now_With_Existing_Repeat_Count Définissez le démarrage à l'heure actuelle et reprogrammant immédiatement la tâche, y compris les ratés.
• Misfire_instruction_reschedule_now_with_remaining_repeat_count similaire à MisfireInstructionRescheDuLenowwithexistingrepeat_count, la différence est que les tâches qui ont déjà été ratées seront ignorées.
• Misfire_instruction_reschedule_next_with_existing_count à l'heure prévue suivante, redémarrez la tâche de répartition, y compris les ratés.
• Misfire_instruction_reschedule_next_with_remaining_count similaire à MisfireInstructionRescheDuleNextwithexistingCount, la différence est que les tâches qui ont déjà été ratées seront ignorées.
• Misfire_instruction_smart_policy Toutes les valeurs par défaut de Trigger de Trigger sont la suivante, ce qui signifie à peu près "laisser la logique de traitement au quartz intelligent pour décider". La stratégie de base est.
• Si le calendrier n'est exécuté qu'une seule fois, utilisez MISFIRE_INSTRUCTION_FIRE_NOW.
• S'il s'agit d'une planification infinie (RepeatCount est infinie), utilisez Misfire_instruction_Reschedule_Next_With_Reminging_Count.
• Sinon, l'utilisation de Misfire_instruction_Reschedule_Now_With_Existing_Repeat_Count Mispus est assez compliquée, vous pouvez vous référer à cet article.
Calendrier
Le calendrier ici n'est pas Java.util.util.calendar de JDK, pas pour calculer les dates. Sa fonction est de compléter le temps du déclencheur. Certains points spécifiques dans le temps peuvent être exclus ou ajoutés.
Prenant "un remboursement automatique des dettes de carte à 0h00 le 25 de chaque mois" à titre d'exemple, nous voulons exclure le temps du 25 février de chaque année (car il y a 2,14, février, il fera certainement faillite). Cette fois peut être réalisée par le calendrier.
exemple:
AnnualCalendar CAL = New AnnualCalendar (); // définir un calendrier exécuté chaque année avec une précision de jours, c'est-à-dire qu'il ne peut être défini qu'à 14h00 le 2.25 java.util.calendar excludeday = new GregoricanCalendar (); excludeday.settime (newdate (). Inmonthonday (2, 25). // Définir la date d'exclusion 2.25 scheduler.addcalendar ("févcal", cal, false, false); // Scheduler rejoint ce calendrier // Définir un TriggerTrigger Trigger = newtrigger (). Withentity ("Trigger1", "Group1") .StartNow () // Une fois que le planificateur est ajouté, il prend effet immédiatement.ModifiedByCalendar ("Febcal") // Utiliser Calendar !! .WithSchedule (SimpleSchedule () .WithIntervalinSeconds (1) .repeatforever ()) .build ();Quartz nous fournit considérablement les calendriers suivants. Notez que tous les calendriers peuvent être exclus ou l'inclusion selon:
• HolidayCalendar. Spécifiez une date spécifique, telle que 20140613. Précision vers le ciel.
• DailyCalendar. Spécifie la période de temps de chaque jour (RangestartingTime, RangendmeTime), le format est HH: MM [: SS [: MMMM]]. Autrement dit, la précision maximale peut atteindre des millisecondes.
• WeeklyCalendar. Spécifiez le jour de la semaine de chaque semaine, la valeur facultative est java.util.calendar.sunday. La précision est le jour.
• MonthlyCalendar. Spécifiez le jour de chaque mois. Les valeurs facultatives sont 1-31. La précision est le jour
• AnnualCalendar. Spécifiez quel jour de l'année. La méthode d'utilisation est comme indiqué dans l'exemple ci-dessus. La précision est le jour.
• Croncalendar. Spécifie une expression cron. La précision dépend de l'expression CRON, c'est-à-dire que la précision maximale peut atteindre des secondes.
Classe d'implémentation déclencheur
Quartz a les implémentations de déclenchement suivantes:
Simpletger
Spécifie les tâches qui commencent à un certain moment et sont effectuées à un certain intervalle de temps (en millisecondes). Il convient aux tâches similaires à: commencer à 9h00 et exécuter toutes les 1 heure. Ses propriétés sont:
• Intervalle de répétition de répétition
• Nombre de répétitions répétitifs. Le nombre réel d'exécutions est RepeatCount + 1. Car il sera exécuté une fois au startime. Il en va de même pour la propriété RepeatCount ci-dessous.
exemple:
CalendaRintervalTrigger
Semblable à SimpleTrigger, il spécifie des tâches qui commencent à un certain moment et sont exécutées à un certain intervalle de temps. Mais la différence est que l'intervalle de temps spécifié par SimpleTrigger est des millisecondes, et il n'y a aucun moyen de spécifier qu'il sera exécuté tous les deux mois (l'intervalle mensuel n'est pas une valeur fixe), tandis que les unités d'intervalle soutenues par CalendaRetervaltrigger incluent des secondes, des minutes, des heures, des jours, des mois, des années et des semaines. Comparé à SimpleTrigger, il présente deux avantages: 1. Il est plus pratique. Par exemple, si vous exécutez toutes les 1 heure, vous n'avez pas à calculer combien de millisecondes 1 heure est égale. 2. Prend en charge des intervalles qui ne sont pas de longueur fixe, tels que des intervalles qui sont des mois et des années. Mais l'inconvénient est que la précision ne peut atteindre que des secondes. Sa tâche appropriée est similaire à: commencer à 9h00 et exécuter une fois par semaine à 9h00. Ses propriétés sont:
• Intervalle d'exécution d'intervalle
• Unités d'intervalle d'intervalle d'exécution (secondes, minutes, heures, jours, mois, années, semaines)
exemple:
CalendaRIntervalSchedule () .WithIntervalindays (1) // Execute une fois par jour.build (); calendaRIntervalSchedule () .WithIntervalinWeeks (1) // Exécuter une fois par semaine.build ();
DailyTimeIntervalTrigger
Spécifiez que les tâches sont effectuées à certains intervalles pendant une certaine période de temps chaque jour. Et il peut prendre en charge des semaines spécifiées. Sa tâche appropriée est similaire à: spécifier de 9h00 à 18h00 chaque jour, exécutée toutes les 70 secondes, et seulement du lundi au vendredi. Ses propriétés sont:
• Heure de début Startimeofday tous les jours
• Heure de fin de la journée de la journée
• Daysofweek la semaine à exécuter
• Intervalle d'exécution d'intervalle
• Unités d'intervalle d'intervalle d'exécution (secondes, minutes, heures, jours, mois, années, semaines)
• Nombre de répétitions répétitifs
exemple:
DailyTimeIntervalSchedule () .StartingDailyat (Timeofday.HourAndMinuteOfday (9, 0)) // commence à 9h00 le jour.endingDailyat (Timeofday.HourandMinuteOfday (16, 0)) // End à 16: 00.OndaysoftHeweek (lundi, mercredi, vendredi) // EXECUTS de lundi de vendredi. IntervalinHours (1) // exécuter toutes les 1 heure. AvecrepeatCount (100) // répéter jusqu'à 100 fois (en fait exécuté 100 + 1 fois) .build (); DailyTimeIntervalSchedule () .StartingDailyat (Timeofday.HourAndMinuteOfday (9, 0)) // commence à 9:00 le jour. Cette méthode calcule en fait la fin de la journée basée sur StarTimeOfday + Interval * Count .ondaysoftheweek (lundi, mardi, mercredi, jeudi, vendredi) // Exécuter du lundi au vendredi.
Ajoutant
Convient pour des tâches plus complexes, il prend en charge la syntaxe tapée sur Linux Cron (et est plus puissant). Fondamentalement, il couvre la plupart (mais pas tous) des trois déclencheurs ci-dessus - bien sûr, il est également plus difficile à comprendre. Ses tâches appropriées sont similaires à: chacune une fois par jour à 0h00, 9h00 et 18h00. Ses propriétés ne sont que:
Expressions cron
Mais cette représentation elle-même est suffisamment complexe. Il y aura des instructions ci-dessous. exemple:
CRONSCHEDULE ("0 0/2 8-17 * *?") // Exécuter toutes les 2 minutes à 8 h 00-17: 00 Every Day.build (); CRONSCHEDULE ("0 30 9? * Mon") // EXÉCUTER tous les lundis à 9 h 30.Build (); WeeklyOndayandHourandMinute (Lundi 9, 30) // équivalent à 0 30 9? * Lun .build ();Expressions cron
| Emplacement | Domaine temporel | Valeurs autorisées | Valeur spéciale |
| 1 | Deuxième | 0-59 | , - * / |
| 2 | minute | 0-59 | , - * / |
| 3 | Heure | 0-23 | , - * / |
| 4 | date | 1-31 | , - *? / LWC |
| 5 | mois | 1-12 | , - * / |
| 6 | Semaine | 1-7 | , - *? / LC # |
| 7 | Année (facultative) | 1-31 | , - * / |
• asterrisk (): peut être utilisé dans tous les champs pour représenter chaque instant dans le domaine temporel correspondant, par exemple, dans le champ minute, cela signifie "par minute";
• Marque d'interrogation (?): Ce caractère n'est utilisé que dans les champs de date et de semaine, et il est généralement spécifié comme une "valeur dénuée de sens", équivalent à un caractère DOT;
• MINUS SIGN (-): exprime une plage. Si "10-12" est utilisé dans le champ Hour, cela signifie de 10 à 12 points, c'est-à-dire 10,11,12;
• Comma (,): exprime une valeur de liste. Si "lun, mer, ven" est utilisé dans le terrain de la semaine, cela signifie lundi, mercredi et vendredi;
• Slash (/): x / y représente une séquence de pas égale, x est la valeur de départ et y est la valeur étape incrémentielle. Si vous utilisez 0/15 dans le champ Minute, il est exprimé comme 0, 15, 30 et 45 secondes, tandis que 5/15 signifie 5, 20, 35, 50 dans le champ minute, vous pouvez également utiliser * / y, ce qui équivaut à 0 / Y;
• L: Ce caractère n'est utilisé que dans les champs de date et de semaine, représentant la signification de "dernier", mais cela signifie différemment dans les deux champs. L dans le champ Date indique le dernier jour du mois, comme le 31 janvier et le 28 février, ce qui n'est pas une année de saut; Si L est utilisé dans la semaine, il indique samedi, ce qui équivaut à 7. Cependant, si L apparaît dans le champ de la semaine et est précédé d'une valeur x, cela signifie "les derniers jours du mois", par exemple, 6L signifie le dernier vendredi du mois;
• W: Ce personnage ne peut apparaître que dans le champ Date et est une modification de la date de tête, indiquant la journée de travail la plus proche de la date. Par exemple, 15W représente la journée de travail la plus proche du 15 du mois. Si le 15 du mois est samedi, il correspond vendredi 14; Si le 15 du mois est dimanche, il correspond lundi 16; Si le 15 du mois est mardi, c'est mardi 15. Cependant, il faut noter que la date de correspondance associée ne peut pas être franchie au mois. Si vous spécifiez 1W, si le 1er jour est samedi, le résultat correspond lundi 3, pas le dernier jour du dernier mois. Une chaîne W ne peut spécifier qu'une seule date, mais ne peut pas spécifier une plage de dates;
• Combinaison LW: LW peut être utilisé dans le champ Date, ce qui signifie le dernier jour ouvrable du mois; Signe de livre (#): ce personnage ne peut être utilisé que dans le champ de la semaine, représentant une journée de travail du mois. Par exemple, le 6 # 3 représente le troisième vendredi du mois (6 représente le vendredi, # 3 représente le troisième pour le moment), tandis que 4 # 5 représente le cinquième mercredi du mois, en supposant que le mois n'a pas le cinquième mercredi, il est ignoré et non déclenché;
• C: Ce caractère n'est utilisé que dans les champs de date et de semaine, représentant la signification du "calendrier". Cela signifie la date associée au plan, et si la date n'est pas associée, elle équivaut à toutes les dates du calendrier. Par exemple, 5C dans le champ Date est équivalent au premier jour après le 5ème jour du calendrier. 1C équivaut au premier jour après dimanche dans le terrain de la semaine.
Les expressions Cron ne sont pas sensibles au cas des caractères spéciaux et ne sont pas sensibles à l'abréviation du cas anglais de la semaine. Quelques exemples:
| Expression | illustrer |
| 0 0 12 * *? | Courez à 12 heures tous les jours |
| 0 15 10? * * | Courez à 10h15 tous les jours |
| 0 15 10 * *? | Courez à 10h15 tous les jours |
| 0 15 10 * *? * | Courez à 10h15 tous les jours |
| 0 15 10 * *? 2008 | Courir à 10h15 par jour en 2008 |
| 0 * 14 * *? | Se déroule toutes les minutes entre 14h00 et se termine à 14h59 tous les jours. |
| 0 0/5 14 * *? | Courez toutes les 5 minutes de 14h00 à 15h00 tous les jours, à partir de 14h55 et se terminant à 14h55. |
| 0 0/5 14,18 * *? | Il dure toutes les 5 minutes de 14h00 à 15h00 tous les jours, et il dure toutes les 5 minutes de 18h00 à 19h00 tous les jours. |
| 0 0-5 14 * *? | Courez chaque minute de 14h00 à 14h05 tous les jours. |
| 0 10 44 14? 3 mer | Courez une fois par minute tous les mercredis de 14h10 à 14h44. |
| 0 15 10? * Lun-vendre | Courez tous les lundis, mardi, mercredis, jeudis, jeudis et vendredis à 10h15. |
| 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次,那么就有可能多个任务被并发执行。
Sometimes we don’t want to execute tasks concurrently, for example, this task needs to "get a list of all unsent mails in the database". If it is executed concurrently, a database lock is needed to avoid a data being processed multiple times. This time a @DisallowConcurrentExecution solves this problem. C'est ça:
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,小心处理。
Autres propriétés
•Durability(耐久性?) 如果一个任务不是durable,那么当没有Trigger关联它的时候,它就会被自动删除。
•RequestsRecovery 如果一个任务是"requests recovery",那么当任务运行过程非正常退出时(比如进程崩溃,机器断电,但不包括抛出异常这种情况),Quartz再次启动时,会重新运行一次这个任务实例。
可以通过JobExecutionContext.isRecovering()查询任务是否是被恢复的。
Planificateur
•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表达式的格式:秒分时日月周年(可选)。
Field name | Allowed value | Allowed special characters ------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
字符含义:
•*:代表所有可能的值。因此,“*”在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. J'espère que vous pourrez vous faire référence et j'espère que vous pourrez soutenir Wulin.com plus.