Uma tarefa de tempo é uma função de especificar um intervalo de tempo futuro para executar determinadas tarefas. Nos aplicativos da Web atuais, a maioria dos aplicativos possui funções de agendamento de tarefas. Eles têm sua própria sintaxe e soluções para diferentes vozes e sistemas operacionais. O sistema operacional Windows chama de planejamento de tarefas e os serviços CRON no Linux fornecem essa função. Essa função geralmente está envolvida em nosso sistema de desenvolvimento de negócios. Este bate -papo usará o idioma Java para concluir o uso de tarefas de tempo comumente usadas no trabalho diário de desenvolvimento, na esperança de ajudar o trabalho e o estudo de todos.
1. Cenários de tarefas cronometrados
(1) Drive Processing Workflow
Como uma nova ordem pré -paga, ele é inicializado e colocado. Se o pedido não for pago dentro do tempo especificado, será considerado que o pedido de tempo limite será fechado. É amplamente utilizado no sistema de comércio eletrônico. Os usuários compram mercadorias e geram pedidos, mas não fazem pagamentos. Se o pedido não for pago em 30 minutos, o pedido será fechado (e o número de pessoas que atingem esse cenário é enorme) e é impossível usar intervenção manual.
(2) Manutenção do sistema
O trabalho de despacho obterá o log de exceção do sistema e armazenará alguns dados de pontos -chave no banco de dados. Ele será despejado no banco de dados às 23:30 todos os dias da semana (exceto feriados e dias da semana) e gerará um arquivo XML e o enviará para o endereço de email de um determinado funcionário.
(3) Forneça serviços de lembrete dentro do aplicativo.
O sistema lembra regularmente ao usuário conectado a executar o trabalho relacionado em um determinado momento.
(4) tarefas de reconciliação cronometradas
A empresa e as empresas de três partes (operadoras, bancos etc.) conduzirão a reconciliação dos negócios no mesmo dia após a meia-noite todos os dias, enviarão os dados do resultado das informações de reconciliação para o endereço de email da pessoa relevante e processará os dados de incompatibilidade durante as horas de trabalho do dia seguinte.
(5) Estatísticas de dados
Existem muitos registros de dados e a leitura e a consulta em tempo real do banco de dados gerarão um certo período de tempo, que é para a experiência do cliente e as necessidades de desempenho. Portanto, os dados são resumidos a cada semana (dias, horas), para que os dados possam ser apresentados rapidamente ao exibir os dados.
Existem muitos cenários em que as tarefas de tempo são usadas ... parece que as tarefas de tempo são realmente amplamente usadas em nosso desenvolvimento diário ...
2. Explicação do timer de tecnologia de Tecnologia de Timing, Mainstream
Eu acredito que todos já estão muito familiarizados com o java.util.timer. É a maneira mais fácil de implementar a programação de tarefas. Aqui está um exemplo específico:
pacote com.ibm.scheduler; importar java.util.timer; importar java.util.timertak; classe pública TimerTest estende TimerTask {private String jobName = ""; public timeTerSest (String JobName) {super (); this.JobName = JobName; } @Override public void run () {System.out.println ("Execute" + JobName); } public static void main (string [] args) {timer timer = new Timer (); atraso longo1 = 1 * 1000; longo período1 = 1000; // Depois de 1 segundo a partir de agora, execute o JOB1 Timer.Schedule (novo TimeTest ("Job1"), Atraso1, Período1); atraso longo2 = 2 * 1000; longo período2 = 2000; // Após 2 segundos a partir de agora, execute o JOB2 Timer.Schedule (novo TimeTest ("Job2"), Atraso2, Período2); }} /**
Resultado da saída:
executar o trabalho1
executar o trabalho1
executar o trabalho2
executar o trabalho1
executar o trabalho1
executar o trabalho2
*/
As classes principais que usam o timer para implementar a programação de tarefas são o timer e o TimerTask. O temporizador é responsável por definir o tempo de execução de início e intervalo do TimerTask. O usuário precisa apenas criar uma classe de herança de TimerTask, implementar seu próprio método de execução e, em seguida, jogá -lo no timer para execução. O núcleo de design do timer é uma lista de tarefas e um TaskThread. O timer lança as tarefas recebidas em sua lista de tarefas, que classifica a lista de tarefas de acordo com o tempo inicial de execução da tarefa. O TimerThread começa a se tornar um tópico daemon ao criar o timer. Este thread pesquisa todas as tarefas, encontra uma tarefa recentemente executada e depois dorme. Quando a hora de início do mais recentemente a ser executada, o TimerThread é despertado e a tarefa é executada. Depois disso, o TimerThread atualiza a tarefa mais recente a ser executada e continua a hibernar.
A vantagem do timer é que ele é simples e fácil de usar, mas como todas as tarefas são agendadas pelo mesmo thread, todas as tarefas são executadas em série e apenas uma tarefa pode ser executada ao mesmo tempo. O atraso ou exceção da tarefa anterior afetará as tarefas subsequentes (esse ponto precisa receber atenção).
ScheduledExecutor
Em vista das deficiências acima do timer, o Java 5 lançou o ScheduleDexector com base no design do pool de threads. A idéia de design é que cada tarefa agendada seja executada por um thread no pool de threads, para que as tarefas sejam executadas simultaneamente e não sejam perturbadas um pelo outro. Deve -se notar que o ScheduedExecutor só iniciará um tópico quando chegar o tempo de execução da tarefa, e o ScheduedExector estiver pesquisando a tarefa pelo resto do tempo.
pacote com.ibm.scheduler; importar java.util.concurrent.executores; importar java.util.concurrent.scheduledExecutorService; importar java.util.Concurrent.TimeUnit; 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 = executores.newscheduledThreadpool (10); Longo InitialDelay1 = 1; longo período 1 = 1; // Depois de 1 segundo a partir de agora, execute o Job1 Service.ScheduleAtFixedRate (New ScheduleDexecutorSt ("Job1"), InitialDelay1, Período1, TimeUnit.Seconds); LONG HOMENDELAY2 = 1; Longo atraso2 = 1; // Após 2 segundos a partir de agora, execute o JOB2 Service.SchedulewithFixedDelay (new ScheduleDexecutorTest ("Job2"), InitialDelay2, Delay2, TimeUnit.Seconds); }}/** Resultado da saída: Execute Job1Execute Job1Execute Job2Execute Job1Execute Job1Execute Job2*/
O código acima mostra dois métodos de agendamento mais comumente usados, ScheduleatFixedrate e SchedulewithFixedDelay, no ScheduleDelta. ScheduleAtFixedrate Cada tempo de execução é um intervalo de tempo que se afasta do início da tarefa anterior, ou seja, cada tempo de execução é: InitialDelay, INICIALDELAY+Período, InitialDelay+2*Período,… SchedulewithFixedDelay, cada tempo de execução é um intervalo de tempo, é um tempo inicial, que é o tempo inicial do final do final da tarefa, que é o tempo de execução, a cada hora é um tempo de tempo, o tempo inicial é que o tempo é um tempo de tempo, a cada hora, o tempo de execução é um intervalo de tempo, o tempo inicial do final do final do final do final do final do final do final do tempo, o tempo de realização do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do final do tempo, o tempo de execução é um intervalo de tempo. InitialDelay+2*ExecuteTime+2*Atraso. Pode -se observar que o ScheduleAtFixedrate é baseado em intervalos de tempo fixo para agendamento de tarefas, e o ScheduleWithFixedDelay depende da duração de cada tempo de execução de tarefas e é baseado em intervalos de tempo não fixados para agendamento de tarefas.
Use o ScheduledeDexecutor e o calendário para implementar o agendamento de tarefas complexas
O timer e o ScheduledExecutor só podem fornecer o agendamento de tarefas com base no intervalo de hora de início e repetição e não são competentes para requisitos de agendamento mais complexos. Por exemplo, defina a tarefa a ser executada toda terça -feira às 16:38:10. Esta função não pode ser implementada diretamente usando o timer ou o ScheduleDexector, mas podemos implementá -lo indiretamente com a ajuda do calendário.
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.concurrent.TimeUnit;public class ScheduledExceutorTest2 extends TimerTask {private String jobName = ""; public ScheduleDexceutorTest2 (String JobName) {super (); this.JobName = JobName; } @Override public void run () {System.out.println ("date ="+new Date ()+", Execute"+JobName); } /** * Calcule a última hora que começa a partir do horário atual e atende às condições da semana, hora de hora, * minutofHour, segundo de minúscula * @return * /public calendar getEarliestdate (calendar currentDate, int mayofweek, hourOfday, Int MinuteOfHour, Int SecofMinite) { Hour_of_day, minuto, segundo, 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 CurrentSecond = currentDate.get (calendar.second); // Se o dia do dia na condição de entrada for menor que a semana da data atual, então WEEM_OF_YEAR precisará ser adiado por uma semana boolean weeklater = false; if (DayOfWeek <CurrentdayOfWeek) {Weeklater = true; } else if (DayOfWeek == CurrentdayOfWeek) {// Quando a condição de entrada é igual à semana da data atual, se // HOMEFDAY na condição de entrada for menor que // atual da data atual, WEEM_OF_YEAR precisa ser adiada por uma semana se (hora de dia <TreinHour) {True; } else if (hourOfday == CurrentHour) {// Quando a condição de entrada é igual à semana do dia, HourOfday da data atual, // se o minutofour na condição de entrada for menor que // atual minuto da data atual, o WEEM_OF_YEAR precisar ser adiado por uma semana se (minuto de hora de corrente) {CurrentMinUte) { } else if (minuteOfHour == CurrentSecond) {// Quando a condição de entrada é igual à semana, hora de dia, // minutofHour, se // segundofminita na condição de entrada é menor que a semana (SecondOfMinite, // então Week_of_year precisa ser pós -pagamento por uma semana se (SecondOfMinite; }}}}} if (weeklater) {// defina week_of_year na data atual para adiar uma semana currentdate.set (calendar.week_of_year, currentweekofyear + 1); } // Defina Day_of_week, hour_of_day, minuto, segundo na data atual como os valores na condição de entrada. currentDate.set (calendar.day_of_week, DayofWeek); currentDate.set (calendar.hour_of_day, hourOfday); currentDate.set (calendário.Minute, MinuteOfHour); CurrentDate.Set (calendário.SECOND, SecondOfminita); retornar currentDate; } public static void main (string [] args) lança Exceção {scheduledExceutorTest2 test = new ScheduleDexceutorTest2 ("Job1"); // obtenha o calendário horário atual date = calendar.getInstance (); long currentDatelong = currentDate.getTime (). getTime (); System.out.println ("date atual =" + currentDate.gettime (). Tostring ()); // Calcule o último tempo de execução que atende ao calendário de condição anteriordate = teste .GereeEarliestDate (CurrentDate, 3, 16, 38, 10); muito anteriordAtelong = EarlyDate.getTime (). gettime (); System.out.println ("DATA ENCI mais antiga =" + anteriordate.getTime (). ToString ()); // Calcule o intervalo de tempo desde o tempo atual até o último tempo de execução Longo Atraso = AnteriorDDelong - CurrentDatelong; // Calcule o período de execução é de um período de um período de longa duração = 7 * 24 * 60 * 60 * 1000; Serviço de ScheduledExecutorService = Executores.NewscheduledThreadpool (10); // a partir de agora atraso milissegundos, execute o JOB1 Service.SCHEDuleAtFixedrate (teste, atraso, período, timeUnit.millisEconds); }}/** Resultado da saída: data atual = Qua 02 de fevereiro 17:32:01 CST 2011Earliest data = Terr.
O código acima implementa a função das tarefas de agendamento às 16:38:10 toda terça -feira. O núcleo é calcular o tempo absoluto de 16:38:10 na última terça -feira com base no horário atual e calcular a diferença de horário a partir do horário atual como um parâmetro para chamar a função ScheduleDExceutor. Para calcular o tempo mais recente, a função de java.util.Calendar é usada. Primeiro, precisamos explicar algumas idéias de design de calendário. O calendário tem as seguintes combinações que identificam exclusivamente uma data:
Citar
Ano + mês + dia_of_month
Ano + mês + semana_of_month + dia_of_week
Ano + mês + dia_of_week_in_month + dia_of_week
Ano + dia_of_year
Ano + dia_of_week + week_of_year
As combinações acima são combinadas com o HourOfday + Minute + Second para ser uma marca de tempo completa.
A demonstração acima adota o último método de combinação. A entrada é Day_of_week, hour_of_day, minuto, segunda e data atual, e a saída é uma data futura que atende a Day_of_week, hour_of_day, minuto, segundo e está mais próximo da data atual. O princípio do cálculo é iniciar a comparação a partir da entrada Day_Of_Week. Se for menor que o dia_of_week da data atual, você precisará aumentar ainda mais o Week_of_year, ou seja, adicione o WEED_OF_YEAR na data atual e substitua o valor antigo; Se for igual ao atual dia_of_week, continue comparando hour_of_day; Se for maior que o atual Day_Of_Week, chame diretamente o calendário.set (campo, value) função de java.util.calenda para atribuir o Day_of_week, hour_of_day, minuto, segundo ao valor de entrada e assim por diante até que a comparação seja alcançada em segundo. Podemos selecionar diferentes combinações com base nos requisitos de entrada para calcular o tempo de execução mais recente.
É bastante pesado implementar o agendamento de tarefas usando o método acima. Esperamos precisar de uma ferramenta de agendamento de tarefas mais completa para resolver esses complexos problemas de agendamento. Felizmente, o quartzo do kit de ferramentas de código aberto mostrou ótimos recursos nesse sentido.
Quartzo
O OpenSymphony Open Source Organization é outro projeto de código aberto no campo da programação de empregos, que pode ser combinada com aplicativos J2EE e J2SE ou usada sozinha. O quartzo pode ser usado para criar programas simples ou complexos que executam dez, centenas ou até dezenas de milhares de empregos.
Vamos dar uma olhada em um exemplo:
pacote com.test.quartz; importar org.quartz.datebuilder.newdate; importar org.quartz.jobbuilder.newjob; importar org.quartz.simpleschedulebuilder.simpleschedule; importar org.ologtz.triggerbger.nnenn; java.util.gregorianCalendar; importar org.quartz.jobdetail; importar org.quartz.scheduler; importar org.quartz.trigger; importar org.quartz.impl.stdschedulerFactory; import.quartz.impl.CalEndar.AnnualCALENDERCALENDENCALENCAL; args) {tente {// crie agendador Agendador Scheduler = stdschedulerFactory.getDefaultScheduler (); // define um gatilho gatilho gatilho = newTrigger (). WithIdentity ("trigger1", "Group1") // Definir nome/grupo .StartNow () // Depois que o agendador é adicionado, ele tem efeito imediatamente. executar, correndo até que ainda não esteja parado.Build (); // Defina um trabalho JobDetail Job = newjob (helloQuartz.class) // Defina a classe de trabalho como a classe HelloQuartz, que é a verdadeira lógica de execução.WithIdentity ("Job1", "Group1") // define o nome/grupo .UsingJobdata ("Nome", "Quartz") // // Adicione a este agendador.schedulejob (trabalho, gatilho); // Iniciar o Scheduler.start (); // Threads fechado.sleep (10000); scheduler.shutdown (true); } catch (Exceção e) {e.printStackTrace (); }}} pacote com.test.quartz; importar java.util.date; importar org.quartz.disallowconcurrentExecution; importar org.quartz.job; import org.quartz.jobdetail; importen; Execute (contexto JobExecutionContext) lança JobExecutionException {JobDetail detalhe = context.getJobDetail (); Nome da sequência = detalhe.getJobDatamap (). GetString ("nome"); System.out.println ("diga olá para" + nome + "em" + new Date ()); }}Através dos exemplos acima: os 3 elementos básicos mais importantes do quartzo:
• Agendador: agendador. Toda a programação é controlada por ele.
• Trigger: define a condição de acionamento. No exemplo, seu tipo é o SimpleTrigger, que é executado a cada 1 segundo (o que é o SimpleTrigger, será descrito em detalhes abaixo).
• JobDetail & Job: JobDetail define dados de tarefas, e a lógica de execução real está no trabalho, no exemplo, HelloQuartz. Por que é projetado como um trabalho de trabalho + trabalho e não usa diretamente o trabalho? Isso ocorre porque é possível executar tarefas simultaneamente. Se o Scheduler usar trabalhos diretamente, haverá um problema de acesso simultâneo à mesma instância de trabalho. No método JobDetail & Job, sempre que o Sheduler é executado, ele criará uma nova instância de trabalho com base no banco de dados, para que o problema do acesso simultâneo possa ser evitado.
API de quartzo
O estilo da API do Quartz é depois do 2.x e adota o estilo DSL (geralmente significa estilo de interface fluente), que é a parte newTrigger () no exemplo. É implementado através do construtor, que é o seguinte. (A maioria dos códigos a seguir se refere a esses construtores)
// Builderport Related Builderport Org.Quartz.jobbuilder. org.quartz.DailyTimeIntervalScheduleBuilder.*; importar org.quartz.calendarintervalscheduleBuilder. Compare: JobDetail jobDetail = new JobDetailImpl ("JobDetail1", "Group1", helloQuartz.class); JobDetail.getjobdatamap (). Put ("name", "quartz"); simplretrigger trigger = new triggerImpl ("trigger1" "" "; Date ()); trigger.setRepeatInterval (1); trigger.setRepeatCount (-1);Sobre nome e grupo
JobDetail e Trigger têm nome e grupo.
O nome é o identificador único neste Sheduler. Se queremos atualizar uma definição de JobDetail, precisamos definir uma instância do JobDetail com o mesmo nome.
O Grupo é uma unidade de organização e Sheduler fornecerá algumas APIs para todo o grupo de operações, como o Scheduler.ResumeJobs ().
Acionar
Antes de começar a explicar cada gatilho em detalhes, você precisa entender algumas semelhanças de gatilho.
StartTime & Endtime
O intervalo de tempo especificado pelo Starttime e Endtime será acionado. Fora desse intervalo, o gatilho não será acionado. Todos os gatilhos conterão essas duas propriedades.
Prioridade
Quando o agendador está ocupado, vários gatilhos podem ser acionados ao mesmo tempo, mas os recursos são insuficientes (como pool de encadeamentos insuficientes). Então, neste momento, uma maneira melhor do que um pano de pedra de tesoura é estabelecer prioridade. Execute a prioridade primeiro. Deve -se notar que a prioridade funcionará apenas entre os gatilhos executados ao mesmo tempo, se um gatilho for 9:00 e o outro gatilho é 9:30. Então, por mais alto que seja a próxima prioridade, a anterior será executada primeiro. O valor padrão da prioridade é 5 e o valor padrão é usado quando é negativo. O valor máximo não parece ser especificado, mas é recomendável seguir o padrão Java e usar 1-10. Caso contrário, se você souber se há um valor maior nele quando você vê [a prioridade é 10].
Fiscal (Miss Trigger) Estratégia
Quando recursos de agendadores semelhantes são insuficientes ou quando a máquina trava e reinicia, é possível que alguns gatilhos não sejam acionados no momento em que devem ser acionados, ou seja, Miss Fire. Neste momento, o Trigger precisa de uma estratégia para lidar com essa situação. As estratégias opcionais para cada gatilho variam. Aqui estão dois pontos para prestar atenção:
O gatilho de falhas tem um valor limite, que é configurado na loja de trabalho. Mais que o ramjobstore é org.quartz.jobstore.misfirethreshold. A falha de ignição será contada apenas como excedendo esse limiar. Menos do que esse limiar, o quartzo será recuperado. Todas as estratégias de ignição realmente respondem duas perguntas:
• Isso tem uma falha de ignição precisa ser acionada novamente?
• Se ocorrer falha de ignição, você deseja ajustar o horário de agendamento existente?
Por exemplo, a estratégia de falhas de ignição da SimpleTrigger inclui:
• Misfire_instruction_ignore_misfire_policy Isso não significa ignorar o gatilho perdido, mas ignorar a política de falha. Ele recuperará todas as tarefas de ignição quando o recurso for apropriado e não afetará o tempo de agendamento existente. Por exemplo, o SimpleTrigger é executado a cada 15 segundos e tem 5 minutos no meio e perde 20 falta. Após 5 minutos, assumindo que os recursos sejam suficientes e a tarefa permita a simultaneidade, ela será acionada ao mesmo tempo. Esta propriedade é aplicável a todos os gatilhos.
• Misfire_instruction_fire_now Ignore tarefas que foram falhas e executam horários imediatamente. Isso geralmente se aplica apenas a tarefas que são executadas apenas uma vez.
• Misfire_instruction_reschedule_now_with_existing_repeat_count Defina o tempo inicial para a hora atual e reagelei imediatamente a tarefa, incluindo falhas.
• Misfire_instruction_reschedule_now_with_remaining_repeat_count semelhante ao incorretamente insuficientescheduleNowWithExistingRepeat_Count, a diferença é que as tarefas que já foram incorretas serão ignoradas.
• Misfire_instruction_reschedule_next_with_existing_count No próximo horário programado, reinicie a tarefa de despacho, incluindo falhas.
• Misfire_instruction_reschedule_next_with_remaining_count semelhante à insuficiência insuficientereschedulenextWithExistingCount, a diferença é que as tarefas que já foram falhas serão ignoradas.
• Misfire_instruction_smart_policy Todos os valores padrão de ignição do gatilho são esse, o que significa aproximadamente "deixe a lógica de processamento no quartzo inteligente para decidir". A estratégia básica é.
• Se o cronograma for executado apenas uma vez, use Misfire_instruction_fire_now.
• Se for um agendamento infinito (o RepeatCount é infinito), use misfire_instruction_reschedule_next_with_remaining_count.
• Caso contrário, usando Misfire_instruction_reschedule_now_with_existing_repeat_count falhas é bastante complicado, você pode consultar este artigo.
Calendário
O calendário aqui não é Java.util.Calendar da JDK, não para calcular datas. Sua função é complementar o tempo do gatilho. Certos pontos específicos no tempo podem ser excluídos ou adicionados.
Tomando "reembolso automático de dívidas de cartões às 0:00 do dia 25 de cada mês" como exemplo, queremos descartar o ponto de tempo de 25 de fevereiro de cada ano (porque há 2,14, definitivamente será falido). Desta vez, pode ser alcançado pelo calendário.
exemplo:
Anualcalendar cal = new anualCalendar (); // define um calendário executado todos os anos com precisão de dias, ou seja, não pode ser definido até as 14:00 no 2,25 java.util.calendar excludeday = new Gregoriancalendar (); excludeday.settime (newdate (). // Defina a data de exclusão 2.25 Scheduler.addcalendar ("febcal", cal, false, false); // Scheduler ingressa neste calendário // define um trigger trigger = newTrigger (). WithIdentity ("trigger1", "Group1") .startNow () // Depois que o agendador é adicionado, ele tem efeito imediatamente. .WithSchedule (SimpleSchedule () .WithInterValInsEconds (1) .RepeatForever ()) .Build ();O Quartzo nos fornece considerava os seguintes calendários. Observe que todos os calendários podem estar excluindo ou inclusão dependendo de:
• HolidayCalendar. Especifique uma data específica, como 20140613. Precisão para o céu.
• Dailycalendar. Especifica o período de cada dia (RangestartingTime, RangendingTime), o formato é HH: MM [: SS [: mmmm]]. Ou seja, a precisão máxima pode atingir milissegundos.
• WeeklyCalendar. Especifique o dia da semana de cada semana, o valor opcional é java.util.calendar.sunday. Precisão é dia.
• Monthlycalendar. Especifique o dia de cada mês. Os valores opcionais são 1-31. Precisão é dia
• AnualCalendar. Especifique em que dia do ano. O método de uso é como mostrado no exemplo acima. Precisão é dia.
• Croncalendar. Especifica uma expressão de Cron. A precisão depende da expressão de Cron, ou seja, a precisão máxima pode atingir segundos.
Classe de implementação de acionamento
O quartzo tem as seguintes implementações de gatilho:
SimleLetRigger
Especifica tarefas que começam em um determinado horário e são executadas em um determinado intervalo de tempo (em milissegundos). É adequado para tarefas semelhantes a: comece às 9:00 e execute a cada 1 hora. Suas propriedades são:
• Repetir interval de repetição
• Repetição de repetições de repetições. O número real de execuções é repetição+1. Porque será executado uma vez no início. O mesmo se aplica à propriedade RepeatCount abaixo.
exemplo:
CalendarIntervalTrigger
Semelhante ao SimpleTrigger, ele especifica tarefas que começam em um determinado horário e são executadas em um determinado intervalo de tempo. Mas a diferença é que o intervalo de tempo especificado pelo SimpleTrigger é de milissegundos e não há como especificar que ele será executado a cada dois meses (o intervalo mensal não é um valor fixo), enquanto as unidades de intervalo apoiadas pelo calendário -intervaltrigger incluem segundos, minutos, dias, dias, meses, meses, anos e semanas. Comparado com o SimpleTrigger, ele tem duas vantagens: 1. É mais conveniente. Por exemplo, se você executar a cada 1 hora, não precisará calcular quantos milissegundos 1 hora são iguais. 2. Suporta intervalos que não são de comprimento fixo, como intervalos que são meses e anos. Mas a desvantagem é que a precisão pode atingir apenas segundos. Sua tarefa adequada é semelhante a: comece às 9:00 e execute uma vez por semana às 9:00. Suas propriedades são:
• Intervalo de execução do intervalo
• Unidades de intervalos de intervalos de intervalo de execução (segundos, minutos, horas, dias, meses, anos, semanas)
exemplo:
calendarintervalChedule () .WithIntervalindas (1) // Execute uma vez por dia.build (); calendarintervalSchedule () .WithIntervalinweeks (1) // Execute uma vez por semana.build ();
DailyTimeIntervalTrigger
Especifique que as tarefas sejam executadas em determinados intervalos durante um certo período de tempo todos os dias. E pode suportar semanas especificadas. Sua tarefa adequada é semelhante a: especificando das 9:00 às 18:00 todos os dias, executada a cada 70 segundos e apenas de segunda a sexta -feira. Suas propriedades são:
• StartTimeofday de início da hora todos os dias
• Fim da hora final do dia do dia
• dias da semana a semana a ser executada
• Intervalo de execução do intervalo
• Unidades de intervalos de intervalos de intervalo de execução (segundos, minutos, horas, dias, meses, anos, semanas)
• Repetição de repetições de repetições
exemplo:
DailyTimeIntervalChedule () .StartingDailyAt (TimeOfday.HourandMinuteOfday (9, 0)) // Comece às 9:00 no dia.EningDailyat (TimeOfday.HourandMinuteOfday (16, 0)) // End às 16: 00. dentro do interior (1) // execute a cada 1 hora. WithPeatCount (100) // Repita até 100 vezes (na verdade executado 100+1 vezes) .Build (); DailyTimeIntervalSchedule () .StartingDailyat (TimeOfday.HourandMinuteOfday (9, 0)) // Começa às 9:00 no dia. Na verdade, esse método calcula o EndTimeOfday com base no início do dia do dia+intervalo*contagem .ONDAYOFTHEEK (segunda, terça -feira, quarta -feira, quinta -feira, sexta -feira) // execute de segunda a sexta
Contrigger
Adequado para tarefas mais complexas, suporta sintaxe digitada ao Linux Cron (e é mais poderoso). Basicamente, ele cobre a maioria (mas não todos) dos três gatilhos acima - é claro, também é mais difícil de entender. Suas tarefas adequadas são semelhantes a: cada uma realizada uma vez por dia às 0:00, 9:00 e 18:00. Suas propriedades são apenas:
Expressões Cron
Mas essa representação em si é complexa o suficiente. Haverá instruções abaixo. exemplo:
cronschedule ("0 0/2 8-17 * *?") // Execute a cada 2 minutos às 8: 00-17: 00 todos os dias. * Mon .Build ();Expressões Cron
| Localização | Domínio do tempo | Valores permitidos | Valor especial |
| 1 | Segundo | 0-59 | , - * / |
| 2 | minuto | 0-59 | , - * / |
| 3 | Hora | 0-23 | , - * / |
| 4 | data | 1-31 | , - *? / Lwc |
| 5 | mês | 1-12 | , - * / |
| 6 | Semana | 1-7 | , - *? / LC # |
| 7 | Ano (opcional) | 1-31 | , - * / |
• Asterisk (): pode ser usado em todos os campos para representar cada momento no domínio do tempo correspondente, por exemplo, no campo minuto, significa "por minuto";
• Marco de interrogação (?): Esse personagem é usado apenas nos campos de data e semana e geralmente é especificado como um "valor sem sentido", equivalente a um caractere de pontos;
• Sign de menos (-): expressa um intervalo. Se "10-12" for usado no campo da hora, significa de 10 a 12 pontos, ou seja, 10,11,12;
• vírgula (,): expressa um valor de lista. Se "Seg, Qua, Sex" é usado no campo da semana, significa segunda, quarta e sexta -feira;
• Slash (/): x/y representa uma sequência de etapa igual, x é o valor inicial e y é o valor da etapa incremental. Se você usar 0/15 no campo minuto, ele será expresso como 0, 15, 30 e 45 segundos, enquanto 5/15 significa 5, 20, 35, 50 no campo minuto, você também pode usar */y, que é equivalente a 0/y;
• L: esse personagem é usado apenas nos campos de data e semana, representando o significado de "último", mas significa diferentemente nos dois campos. L No campo Data, indica o último dia do mês, como 31 de janeiro e 28 de fevereiro, que não é o ano bissexto; Se L for usado na semana, indica sábado, que é equivalente a 7. No entanto, se L aparecer no campo da semana e for precedido por um valor x, significa "os últimos x dias do mês", por exemplo, 6L significa a última sexta -feira do mês;
• W: Esse personagem só pode aparecer no campo Data e é uma modificação da data líder, indicando o dia útil mais próximo da data. Por exemplo, 15W representa o dia útil mais próximo até o dia 15 do mês. Se o dia 15 do mês for sábado, corresponde à sexta -feira 14; Se o dia 15 do mês é domingo, corresponde à segunda -feira 16; Se o dia 15 do mês é terça -feira, é terça -feira o dia 15. No entanto, deve -se notar que a data de correspondência associada não pode ser atravessada para o mês. Se você especificar 1W, se o 1º dia for sábado, o resultado corresponde à segunda -feira 3, não no último dia do último mês. Uma string w pode especificar apenas uma única data, mas não pode especificar um intervalo de data;
• Combinação de LW: LW pode ser usado no campo Data, o que significa o último dia útil do mês; Placa de libra (#): esse personagem só pode ser usado no campo da semana, representando um dia útil do mês. Por exemplo, 6#3 representa a terceira sexta -feira do mês (6 representa sexta -feira,#3 representa o terceiro no momento), enquanto 4#5 representa a quinta quarta -feira do mês, assumindo que o mês não tem a quinta quarta -feira, é ignorado e não acionado;
• C: Esse personagem é usado apenas nos campos de data e semana, representando o significado de "calendário". Isso significa a data associada ao plano e, se a data não estiver associada, é equivalente a todas as datas no calendário. Por exemplo, 5C no campo Data é equivalente ao primeiro dia após o 5º dia do calendário. 1C é equivalente ao primeiro dia após domingo da semana.
As expressões Cron não são sensíveis ao caso de caracteres especiais e não são sensíveis à abreviação do caso em inglês da semana. Alguns exemplos:
| Expressão | ilustrar |
| 0 0 12 * *? | Corra às 12 horas todos os dias |
| 0 15 10? * * | Correr às 10:15 todos os dias |
| 0 15 10 * *? | Correr às 10:15 todos os dias |
| 0 15 10 * *? * | Correr às 10:15 todos os dias |
| 0 15 10 * *? 2008 | Corra às 10:15 por dia em 2008 |
| 0 * 14 * *? | Corre a cada minuto entre 14:00 e termina às 14:59 todos os dias. |
| 0 0/5 14 * *? | Corra a cada 5 minutos das 14:00 às 15:00 todos os dias, a partir das 14:55 e terminando às 14:55. |
| 0 0/5 14,18 * *? | Ele vai a cada 5 minutos das 14:00 às 15:00 todos os dias e dura a cada 5 minutos das 18:00 às 19:00 todos os dias. |
| 0 0-5 14 * *? | Corra a cada minuto das 14:00 às 14:05 todos os dias. |
| 0 10,44 14? 3 Qua | Corra uma vez por minuto toda quarta -feira, de 14:10 às 14:44. |
| 0 15 10? * 2A. A 6A | Corra toda segunda -feira, terça, quarta, quinta, quinta e sexta -feira às 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次,那么就有可能多个任务被并发执行。
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. É isso:
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
•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. Espero que você possa lhe dar uma referência e espero que você possa apoiar mais o wulin.com.