Programação simultânea Java detalhada e agendamento de tarefas

Parte 1: Fundamentos da Programação Simultânea

Antes de discutir a programação simultânea, primeiro você precisa entender alguns conceitos e princípios básicos.

o que é fio

Um thread é uma das unidades básicas de execução em um sistema operacional de computador. Faz parte do processo e da unidade básica de agendamento do sistema operacional. Um processo pode conter vários threads, cada um executando seu próprio código, mas eles compartilham a memória do processo e outros recursos.

Threads geralmente são mais leves que processos, podem ser criados e destruídos mais rapidamente e usam recursos do sistema com mais eficiência. Diferentes threads podem ser executados simultaneamente, de modo que várias tarefas possam ser executadas ao mesmo tempo. Threads podem ser executados em uma única CPU ou simultaneamente em várias CPUs, permitindo a computação paralela.

Threads são um conceito importante na programação simultânea e são frequentemente usados ​​para criar aplicativos multitarefa. Por exemplo, em um servidor web, cada solicitação pode ser atribuída a um thread separado para processamento, de modo que o servidor possa lidar com várias solicitações ao mesmo tempo.

Em Java, os threads são implementados por meio da classe Thread. Threads podem ser executados em paralelo ou cooperar para completar uma tarefa.

o que é simultaneidade

Em Java, simultaneidade refere-se à execução de vários threads no mesmo programa dentro de um período de tempo. Quando vários threads são executados simultaneamente, eles podem compartilhar alguns recursos (como memória ou arquivos), o que pode causar corridas de dados e outros problemas. Java fornece vários mecanismos para coordenar e controlar a execução entre vários threads para garantir que eles possam acessar adequadamente os recursos compartilhados.

o que é sincronização

A sincronização é um mecanismo usado para garantir que vários threads não acessem um recurso compartilhado ao mesmo tempo. Em Java, um bloco de código pode ser marcado como sincronizado usando a palavra-chave sincronizada, garantindo assim que apenas um thread possa acessar o bloco de código a qualquer momento. A palavra-chave sincronizada pode ser usada em métodos de instância, métodos estáticos e blocos de código.

o que é mutex

A exclusão mútua é um mecanismo usado para garantir que apenas um thread possa acessar um recurso compartilhado a qualquer momento. Em Java, você pode usar o objeto lock (Lock) para obter exclusão mútua. O objeto Lock pode garantir que apenas um thread pode manter o bloqueio, e somente depois que o thread que contém o bloqueio libera o bloqueio, outros threads podem adquirir o bloqueio e acessar o recurso compartilhado. Ao contrário da palavra-chave sincronizada, os objetos Lock podem fornecer recursos mais avançados, como bloqueios reentrantes e bloqueios justos.

Parte II: Threads nativos JDK e agendamento de tarefas

Java fornece uma rica API de programação simultânea, incluindo threads, bloqueios, semáforos, filas de bloqueio, etc. Esta seção fornece uma introdução detalhada ao thread nativo e ao agendamento de tarefas do JDK.

criar tópico

Em Java, existem duas maneiras de criar threads, uma é herdar a classe Thread e a outra é implementar a interface Runnable.

public class MyThread extends Thread {
    public void run() {
        // 线程执行的代码
    }
}

public class MyRunnable implements Runnable {
    public void run() {
        // 线程执行的代码
    }
}

// 创建线程并启动
MyThread thread = new MyThread();
thread.start();

MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();

estado do encadeamento

Em Java, os threads têm vários estados, incluindo novo, em execução, bloqueado, em espera, em espera cronometrada e encerrado.

  • NOVO: Novo estado, o objeto thread foi criado, mas o método start() não foi chamado para iniciar o thread.
  • RUNNABLE: Estado de execução, o thread está em execução ou aguardando um intervalo de tempo da CPU.
  • BLOQUEADO: Estado bloqueado, o thread está aguardando para adquirir um bloqueio exclusivo ou aguardando a conclusão da operação de E/S.
  • WAITING: Estado de espera, o thread está aguardando notificação de outros threads e está em um estado de espera indefinido.
  • TIMED_WAITING: Estado de espera cronometrado, o thread está aguardando notificação de outros threads, mas aguarda no máximo um período de tempo.
  • TERMINADO: Estado encerrado, o thread concluiu a execução ou foi interrompido.

Diagrama de estado do thread:

+----------------+     +----------------+     +-----------------+
| NEW            | --> | RUNNABLE       | --> | BLOCKED         |
|                |     |                |     |                 |
|                |     |                |     | waiting for     |
|                |     |                |     | monitor lock    |
+----------------+     +----------------+     +-----------------+
       |                      |                           |
       |                      |                           |
       |                      |                           |
       |                      |                           |
       V                      V                           V
+----------------+     +----------------+     +-----------------+
| TERMINATED     | <-- | TIMED_WAITING  | <-- | WAITING         |
|                |     |                |     |                 |
|                |     |                |     | waiting for     |
|                |     |                |     | another thread  |
|                |     |                |     | or I/O to finish|
+----------------+     +----------------+     +-----------------+

sincronização de thread

A sincronização de threads é um mecanismo para garantir a ordem correta de acesso a recursos compartilhados entre vários threads. Java fornece uma variedade de maneiras de obter sincronização de thread, incluindo sincronizado, Lock, Semaphore , etc.

sincronizado

Sincronizado é o mecanismo de sincronização de thread mais comumente usado em Java, que pode garantir que apenas um thread acesse recursos compartilhados ao mesmo tempo. sincronizado pode ser usado em métodos ou blocos de código, ele consegue a sincronização adquirindo o bloqueio do objeto.

public class Counter {
    private int count = 0;
    public synchronized void increment() {
        count++;
    }
    public synchronized void decrement() {
        count--;
    }
}

No código acima, os métodos increment() e decrement() são síncronos, eles irão adquirir o bloqueio do objeto Counter e então realizar a operação count++ ou count--. Isso garante que o acesso à variável de contagem por vários threads seja seguro.

Trancar

Lock é outro mecanismo de sincronização de threads comumente usado em Java, que fornece mais funções e flexibilidade. O bloqueio pode ser usado para implementar a sincronização e também para implementar um mecanismo de espera/notificação. Ao contrário de sincronizado, Lock não é uma palavra-chave no nível da linguagem Java, mas uma classe Java. Lock fornece uma variedade de métodos para obter sincronização de thread, incluindo lock(), tryLock(), lockInterruptably(), newCondition(), etc.

public class Counter {
    private int count = 0;
    private Lock lock = new ReentrantLock();
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
    public void decrement() {
        lock.lock();
        try {
            count--;
        } finally {
            lock.unlock();
        }
    }
}

No código acima, os métodos increment() e decrement() são implementados usando Lock. Eles adquirirão um objeto de bloqueio e, em seguida, executarão operações count++ ou count--. Ao contrário do sincronizado, o uso do Lock requer aquisição e liberação explícitas de bloqueios.

Semáforo

Semáforo é uma ferramenta de sincronização comumente usada em Java, que pode controlar quantos threads acessam recursos compartilhados ao mesmo tempo. O Semaphore fornece dois métodos comumente usados, adquirir () e liberar (), usados ​​para adquirir e liberar a licença.

Aqui está um exemplo simples de uso de um semáforo onde há um recurso compartilhado que vários threads precisam acessar. No entanto, apenas dois threads podem acessar o recurso ao mesmo tempo.

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    // 定义 Semaphore,初始许可数为 2
    private static final Semaphore sem = new Semaphore(2);

    public static void main(String[] args) {
        // 创建 5 个线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    // 尝试获取许可
                    sem.acquire();
                    System.out.println("Thread " + Thread.currentThread().getName() + " acquired permit.");
                    // 模拟线程执行一段时间
                    Thread.sleep(2000);
                    System.out.println("Thread " + Thread.currentThread().getName() + " releasing permit.");
                    // 释放许可
                    sem.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

No exemplo acima, o número de permissão inicial do Semáforo é 2, o que significa que apenas no máximo dois threads podem acessar o recurso compartilhado ao mesmo tempo. Cada thread primeiro chama o método adquire() para tentar adquirir uma licença e, se nenhuma permissão estiver disponível, o thread é bloqueado até que uma licença esteja disponível. Depois que o thread obtém a permissão, ele executa um trecho de código que simula o trabalho e, em seguida, libera a permissão. Quando um thread libera a permissão, outro thread tem a oportunidade de adquirir a permissão e continuar a execução. Como o Semáforo limita o número de permissões, apenas dois threads podem acessar o recurso compartilhado ao mesmo tempo.

Grupo de discussão

O pool de threads é um método de gerenciamento de threads comumente usado, que pode evitar a sobrecarga da criação e destruição frequente de threads e pode controlar o número de threads para garantir a estabilidade e eficiência do sistema. Java fornece a classe ThreadPoolExecutor para implementar a função do pool de threads.

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            Runnable task = new Task();
            executor.execute(task);
        }
        executor.shutdown();
    }
}

No código acima, um pool de threads com tamanho 5 é criado e 10 tarefas são enviadas. Cada tarefa é uma classe que implementa a interface Runnable. Após executar o método executor.execute(task), o pool de threads agendará automaticamente os threads para executar a tarefa. Depois de executar o método executor.shutdown(), o pool de threads será encerrado e aguardará a conclusão de todas as tarefas.

agendamento de tarefas

O agendamento de tarefas refere-se à execução de tarefas específicas em horários ou condições específicas. Java fornece uma variedade de métodos de agendamento de tarefas, incluindo Timer, ScheduledExecutorService, etc.

Cronômetro

Timer é um agendador de tarefas que vem com Java, que pode executar tarefas específicas em horários especificados. O Timer fornece dois métodos, Schedule() e ScheduleAtFixedRate(), que são usados ​​para executar tarefas únicas e tarefas cíclicas.

public class TimerDemo {
    public static void main(String[] args) {
        TimerTask task = new TimerTask() {
            public void run() {
                System.out.println("task is running");
            }
        };
        Timer timer = new Timer();
        timer.schedule(task, 5000);
    }
}

No código acima, um objeto Timer e um objeto TimerTask são criados e, em seguida, o método timer.schedule(task, 5000) é chamado para executar a tarefa após 5 segundos. O Timer também fornece outros métodos, como agendaAtFixedRate() para executar tarefas cíclicas.

ScheduledExecutorService

ScheduledExecutorService é o agendador de tarefas recomendado em Java, que fornece um método de agendamento de tarefas mais flexível e confiável do que o Timer. ScheduledExecutorService fornece dois métodos, Schedule() e ScheduleAtFixedRate(), que são usados ​​para executar tarefas únicas e tarefas cíclicas.

public class ScheduledExecutorDemo {
    public static void main(String[] args) {
        Runnable task = new Task();
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.schedule(task, 5, TimeUnit.SECONDS);
        executor.shutdown();
    }
}

No código acima, um objeto ScheduledExecutorService e um objeto de tarefa são criados e, em seguida, o método executor.schedule(task, 5, TimeUnit.SECONDS) é chamado para executar a tarefa após 5 segundos. ScheduledExecutorService também fornece outros métodos, como scheduleWithFixedDelay() para executar tarefas cíclicas.

Use uma biblioteca de classes de terceiros

Além das próprias funções de thread e agendamento de tarefas do JDK, Java também possui muitas bibliotecas excelentes de terceiros que podem ser usadas para implementar programação simultânea e agendamento de tarefas. Bibliotecas de classes comuns de terceiros incluem:

  • Apache Commons Lang
  • Goiaba
  • Quartzo
  • Tarefa Primavera

Aqui, Quartz e Spring Task são tomados como exemplos para apresentar como usar essas duas bibliotecas de classes para implementar programação simultânea e agendamento de tarefas.

Quartzo

Quartz é uma estrutura de agendamento de tarefas de código aberto, que fornece funções de agendamento de tarefas mais ricas e flexíveis e suporta agendamento de tarefas distribuídas e agendamento de tarefas de cluster. O Quartz pode ser usado para realizar tarefas únicas e cíclicas, bem como tarefas complexas, como envio de e-mails, backup de dados e assim por diante.

Os recursos do quartzo incluem:

  1. Alta flexibilidade: o Quartz pode agendar tarefas de acordo com vários tipos de gatilhos (como gatilhos simples, gatilhos cron, etc.) e também pode configurar atributos como prioridade de execução de tarefas, tempos de execução e tempo.
  2. Alta confiabilidade: O Quartz fornece um mecanismo de tratamento e recuperação de erros que pode tratar e recuperar automaticamente quando ocorre uma falha durante a execução da tarefa.
  3. Suporte distribuído e de cluster: O Quartz oferece suporte ao agendamento de tarefas distribuídas e ao agendamento de tarefas de cluster, que podem ser implantados em várias máquinas para melhorar a confiabilidade e escalabilidade das tarefas.

Quartz agenda tarefas agendadas

A seguir está um exemplo Java que usa Quartz para agendamento de tarefas. Ele usa Quartz para agendar uma tarefa cronometrada e gera uma frase a cada 10 segundos:

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;

public class QuartzDemo implements Job {
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("Hello Quartz! " + new Date());
    }

    public static void main(String[] args) throws SchedulerException, InterruptedException {
        // 创建 Scheduler 工厂
        StdSchedulerFactory factory = new StdSchedulerFactory();
        // 创建 Scheduler
        Scheduler scheduler = factory.getScheduler();
        // 创建 JobDetail
        JobDetail jobDetail = JobBuilder.newJob(QuartzDemo.class).build();
        // 创建触发器
        Trigger trigger = TriggerBuilder.newTrigger()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever())
                .build();
        // 将任务和触发器加入 Scheduler
        scheduler.scheduleJob(jobDetail, trigger);
        // 启动 Scheduler
        scheduler.start();
        // 让程序休眠 60 秒
        Thread.sleep(60000);
        // 停止 Scheduler
        scheduler.shutdown();
    }
}

 No exemplo acima, primeiro criamos uma classe Job (QuartzDemo), que implementa a interface Job do Quartz, e reescrevemos o método execute(), que define as tarefas a serem executadas.

Em seguida, usamos o SchedulerFactory do Quartz para criar um Scheduler e criar um objeto JobDetail e um objeto Trigger. JobDetail define o Job que precisa ser executado e Trigger define as condições de disparo do Job.

Finalmente, adicionamos JobDetail e Trigger ao Scheduler e iniciamos o Scheduler. Após o programa dormir por 60 segundos, paramos o Agendador. Durante a execução do programa, as tarefas definidas em Job serão executadas a cada 10 segundos.

Expressões Cron usando Quartz

Além das tarefas agendadas, o Quartz também oferece outros tipos de agendamento de tarefas, como:

  1. Gatilho de expressão Cron: use uma expressão Cron semelhante ao Linux para definir o tempo de execução da tarefa e você pode definir com flexibilidade o tempo de execução e a frequência da tarefa.
  2. Gatilho de calendário: use o calendário para definir o tempo de execução da tarefa e agende a tarefa excluindo algum horário específico.
  3. Ouvinte: O Quartz fornece uma variedade de ouvintes, que podem executar lógica específica antes e depois da execução da tarefa ou antes e depois do disparo do gatilho.
  4. Agendamento de tarefas de cluster: o Quartz oferece suporte ao agendamento de tarefas de cluster entre várias instâncias do Scheduler, melhorando assim a confiabilidade e a escalabilidade das tarefas.
  5. Agendamento de tarefas distribuídas: O Quartz também suporta agendamento de tarefas distribuídas, que pode agendar tarefas para nós remotos para execução.

A seguir está um exemplo Java que usa o gatilho de expressão Cron do Quartz para agendamento de tarefas. Ele usa o Quartz para agendar uma tarefa cronometrada que é executada todos os dias às 9 horas da manhã:

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;

public class QuartzDemo implements Job {
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("Hello Quartz! " + new Date());
    }

    public static void main(String[] args) throws SchedulerException, InterruptedException {
        // 创建 Scheduler 工厂
        StdSchedulerFactory factory = new StdSchedulerFactory();
        // 创建 Scheduler
        Scheduler scheduler = factory.getScheduler();
        // 创建 JobDetail
        JobDetail jobDetail = JobBuilder.newJob(QuartzDemo.class).build();
        // 创建触发器
        Trigger trigger = TriggerBuilder.newTrigger()
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 * * ?"))
                .build();
        // 将任务和触发器加入 Scheduler
        scheduler.scheduleJob(jobDetail, trigger);
        // 启动 Scheduler
        scheduler.start();
        // 让程序休眠 60 秒
        Thread.sleep(60000);
        // 停止 Scheduler
        scheduler.shutdown();
    }
}

No exemplo acima, usamos o CronScheduleBuilder do Quartz para criar um gatilho de expressão Cron que dispara a tarefa todos os dias às 9h.

Elabore a expressão Cron do Quartz

A expressão Cron do Quartz é uma expressão usada para definir o tempo de agendamento de tarefas, que usa sintaxe Cron semelhante ao Linux para definir o tempo e a frequência de execução da tarefa. A sintaxe das expressões Cron é bastante complicada, mas basta conhecer algumas regras básicas de sintaxe. Aqui está um exemplo de uma expressão Cron simples:

0 0 0/1 * * ?   // 每小时执行一次

A expressão Cron acima consiste em 6 campos que representam segundos, minutos, horas, dias, meses e semanas. As regras de sintaxe para cada campo são as seguintes:

  1. Segundos: 0~59, opcional.
  2. Minutos: 0~59, devem ser especificados.
  3. Horas: 0~23, deve ser especificado.
  4. Dia (Dia do mês): 1~31, deve ser especificado.
  5. Mês: 112 ou JANDEC, um deles deve ser especificado.
  6. Dia da semana: 07 ou SUNSAT (0 e 7 representam domingo), um dos quais deve ser especificado.

Dentre eles, os dois campos mês e semana são mutuamente exclusivos, ou seja, uma tarefa só pode ser executada em um determinado mês ou semana.

Além das regras gramaticais básicas, as expressões Cron também suportam alguns símbolos e palavras-chave especiais, que são usados ​​para representar alguns períodos de tempo e condições lógicas comumente usados. A seguir estão alguns símbolos e palavras-chave especiais comumente usados:

  1. Asterisco (*): Indica qualquer valor e pode ser utilizado em qualquer campo.
  2. Ponto de interrogação (?): Indica que nenhum valor foi especificado e pode ser utilizado nos campos de dia e semana.
  3. Barra (/): Usada para especificar um incremento, por exemplo, */5 significa executar a cada 5 unidades.
  4. Vírgula (,): Usada para especificar vários valores, por exemplo, 1,2,3 significa que todos os três valores de 1, 2 e 3 podem ser executados.
  5. Hífen (-): Usado para especificar um intervalo, por exemplo, 10-15 significa que valores dentro do intervalo de 10 a 15 podem ser executados.

Aqui estão alguns exemplos de expressões Cron:

  • ​​​​:​0 0 0/1 * * ?​ Execute a cada hora.
  • ​​​​:​0 0 12 ? * MON-FRI​ Executar uma vez todos os dias da semana às 12h.
  • ​​​​:​0 0 1 ? * SAT#3​ Executado à 1h do terceiro sábado de cada mês.

Deve-se observar que a sintaxe das expressões cron é muito complexa e precisa ser escrita de acordo com requisitos específicos, sendo recomendável consultar a documentação oficial para aprendizado e uso.

Tarefa Primavera

Spring Task é uma estrutura de agendamento de tarefas baseada em anotações no Spring Framework, que fornece funções simples de agendamento de tarefas e oferece suporte a tarefas únicas e recorrentes. Os principais componentes do Spring Task são o agendador de tarefas (TaskScheduler) e o executor de tarefas (TaskExecutor), onde o agendador de tarefas é responsável por gerenciar o agendamento de tarefas, e o executor de tarefas é responsável pela execução de tarefas.

Os principais recursos do Spring Task incluem:

  1. Suporte para tarefas agendadas e tarefas periódicas: Spring Task oferece suporte a uma variedade de métodos de acionamento de tarefas, incluindo atraso fixo, taxa fixa e expressões Cron.
  2. Suporte para execução assíncrona: Spring Task suporta a execução de tarefas em threads separados sem bloquear o thread principal.
  3. Filtragem de tarefas de suporte: Spring Task suporta filtragem de tarefas com base no nome da tarefa, nome do grupo e expressão Cron.

Tarefas de atraso fixo, tarefas de taxa fixa e tarefas de expressão Cron

A seguir está um código de exemplo simples da Spring Task:

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class MyTask {

    @Scheduled(fixedDelay = 1000)
    public void myFixedDelayTask() {
        System.out.println("Fixed delay task - " + System.currentTimeMillis());
    }

    @Scheduled(fixedRate = 2000)
    public void myFixedRateTask() {
        System.out.println("Fixed rate task - " + System.currentTimeMillis());
    }

    @Scheduled(cron = "0/5 * * * * ?")
    public void myCronTask() {
        System.out.println("Cron task - " + System.currentTimeMillis());
    }
}

O código acima define uma tarefa Spring chamada MyTask, que contém três métodos para definir tarefas de atraso fixo, tarefas de taxa fixa e tarefas de expressão Cron. Todos esses métodos usam a anotação @Scheduled fornecida pelo Spring Task para especificar como a tarefa é acionada.

Entre eles, os atributos fixaDelay e fixaRate na anotação @Scheduled são usados ​​para especificar o intervalo de disparo de tarefas de atraso fixo e taxa fixa respectivamente, em milissegundos. O atributo cron é usado para especificar o tempo de disparo da tarefa de expressão Cron. No exemplo acima, as tarefas de atraso fixo e taxa fixa seriam executadas a cada 1 segundo e 2 segundos, respectivamente, enquanto a tarefa de expressão cron seria executada a cada 5 segundos.

Deve-se observar que para habilitar a função Spring Task, a anotação @EnableScheduling precisa ser adicionada à classe principal do aplicativo Spring Boot. Esta anotação pode ativar automaticamente o Spring Task quando o aplicativo é iniciado.

Agendador de tarefas personalizado e executor de tarefas

Também podemos usar as interfaces TaskScheduler e TaskExecutor fornecidas pelo Spring Task para personalizar o agendador e o executor de tarefas. Isso permite um gerenciamento mais flexível do agendamento e execução de tarefas. A seguir está um código de exemplo que usa as interfaces TaskScheduler e TaskExecutor para personalizar o agendador de tarefas e o executor de tarefas:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;

@Configuration
public class MyTaskConfig {

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(10);
        scheduler.setThreadNamePrefix("my-task-scheduler-");
        return scheduler;
    }

    @Bean
    public SimpleAsyncTaskExecutor taskExecutor() {
        SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
        executor.setConcurrencyLimit(5);
        return executor;
    }

    @Bean
    public MyTask myTask() {
        return new MyTask();
    }

    public class MyTask {
        public void runTask() {
            System.out.println("Task is running - " + System.currentTimeMillis());
        }
    }

    public void scheduleTask() {
        TaskScheduler scheduler = taskScheduler();
        SimpleAsyncTaskExecutor executor = taskExecutor();
        MyTask task = myTask();
        scheduler.schedule(() -> executor.execute(task::runTask), new CronTrigger("0/5 * * * * ?"));
    }
}

No código de exemplo acima, primeiro definimos um TaskScheduler e um TaskExecutor, que são usados ​​para personalizar o agendador de tarefas e o executor de tarefas, respectivamente. Entre eles, TaskScheduler usa a implementação ThreadPoolTaskScheduler, que pode configurar parâmetros como tamanho do pool de threads e prefixo do nome do thread, e TaskExecutor usa a implementação SimpleAsyncTaskExecutor, que pode configurar parâmetros como limite de simultaneidade.

A seguir, definimos uma classe MyTask, que contém um método runTask() para executar lógica de tarefa específica. Em seguida, registramos a classe MyTask como Spring Bean por meio da anotação @Bean.

Finalmente, definimos um método scheduleTask() para iniciar o agendamento de tarefas. Este método usa TaskScheduler e CronTrigger para definir o modo de disparo do agendamento de tarefas e usa TaskExecutor para executar a tarefa. Neste exemplo, definimos uma expressão Cron que aciona um agendamento de tarefas a cada 5 segundos. Quando o agendamento de tarefas é acionado, o método runTask() de MyTask será chamado para executar uma lógica de tarefa específica.

Deve-se observar que, para habilitar um agendador e executor de tarefas customizado, precisamos registrar a classe MyTaskConfig como um Spring Bean e chamar o método scheduleTask() por meio da anotação @PostConstruct para iniciar o agendamento de tarefas quando o aplicativo for iniciado.

epílogo

Este artigo explica como usar threads para programação simultânea e agendamento de tarefas em Java. O uso de threads pode melhorar a capacidade de processamento simultâneo do programa, e o uso de agendamento de tarefas pode melhorar a capacidade de processamento automático do programa. Este artigo apresenta as funções nativas de thread e agendamento de tarefas do JDK, bem como as bibliotecas de classes de terceiros comumente usadas, Quartz e Spring Task, na esperança de ser útil para os leitores.

No processo de uso de threads e agendamento de tarefas, você precisa prestar atenção a questões como segurança de thread e repetição de tarefas, especialmente em um ambiente multithread, é necessário adicionar bloqueios para garantir a segurança de thread. Ao mesmo tempo, também é necessário estar atento a problemas como vazamento de memória e consumo de recursos, e tentar evitar loops infinitos no programa e a criação de um grande número de threads para evitar travamentos do sistema.

O agendamento de threads e tarefas são funções muito importantes em Java, que requerem estudo e prática cuidadosos para dominar suas principais habilidades tecnológicas e de otimização e melhorar o desempenho e a confiabilidade do programa.

Acho que você gosta

Origin blog.csdn.net/bairo007/article/details/132446401
Recomendado
Clasificación