Rumah  >  Artikel  >  Beberapa kaedah pengaturcaraan tak segerak, berapa ramai yang anda tahu?

Beberapa kaedah pengaturcaraan tak segerak, berapa ramai yang anda tahu?

Java后端技术全栈
Java后端技术全栈ke hadapan
2023-08-15 16:14:14774semak imbas

Pelaksanaan tak segerak tidak asing lagi bagi pembangun Dalam proses pembangunan sebenar, pelaksanaan tak segerak sering digunakan dalam banyak senario Berbanding dengan pelaksanaan segerak, pelaksanaan tak segerak boleh memendekkan masa pautan permintaan.

Contohnya: 「发送短信、邮件、异步更新等」, ini adalah senario biasa yang boleh dilaksanakan secara tak segerak.

8 cara untuk melaksanakan tak segerak

1, Benang

2, Masa Depan

3, Rangka kerja tak segerak CompletableFuture

acara Spring

anno

6 , mesej baris gilir

7, rangka kerja tak segerak pihak ketiga, seperti Hutool's ThreadUtil

8, Jambu batu asynchronous

Apakah itu tak segerak?

Mula-mula, mari kita lihat senario biasa di mana pengguna membuat pesanan:
Beberapa kaedah pengaturcaraan tak segerak, berapa ramai yang anda tahu?
Senario perniagaan

Apakah itu tak segerak?

Dalam operasi penyegerakan, apabila kita melaksanakan menghantar mesej teks, kita mesti menunggu kaedah ini dilaksanakan sepenuhnya sebelum kita boleh melakukan operasi mata hadiah jika tindakan gift

mengambil masa yang lama untuk melaksanakan, menghantar mesej teks akan mengambil masa yang lama Tunggu, ini adalah senario penyegerakan biasa.

Sebenarnya, tidak ada pergantungan antara menghantar mesej teks dan memberi mata melalui asynchronous, kita dapat menyedari bahawa dua operasi memberi mata dan menghantar mesej teks boleh dijalankan pada masa yang sama, seperti: 🎜.
Beberapa kaedah pengaturcaraan tak segerak, berapa ramai yang anda tahu?
Asynchronous

Inilah yang dipanggil asynchronous. Bukankah ia sangat mudah? . gunakan kumpulan benang:

public class AsyncThread extends Thread {

    @Override
    public void run() {
        System.out.println("Current thread name:" + Thread.currentThread().getName() + " Send email success!");
    }

    public static void main(String[] args) {
        AsyncThread asyncThread = new AsyncThread();
        asyncThread.start();
    }
}

Anda boleh merangkum logik perniagaan ke dalam Runnable atau Callable dan menyerahkannya kepada kumpulan thread untuk dilaksanakan. 2. Future asynchronous
private ExecutorService executorService = Executors.newCachedThreadPool();

public void fun() {
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            log.info("执行业务逻辑...");
        }
    });
}
Hasil keluaran:

@Slf4j
public class FutureManager {


    public String execute() throws Exception {


        ExecutorService executor = Executors.newFixedThreadPool(1);
        Future<String> future = executor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {


                System.out.println(" --- task start --- ");
                Thread.sleep(3000);
                System.out.println(" --- task finish ---");
                return "this is future execute final result!!!";
            }
        });


        //这里需要返回值时会阻塞主线程
        String result = future.get();
        log.info("Future get result: {}", result);
        return result;
    }


    @SneakyThrows
    public static void main(String[] args) {
        FutureManager manager = new FutureManager();
        manager.execute();
    }
}

(1) Kelemahan Masa Depan

Kekurangan Masa Depan termasuk perkara berikut: rreee

t:

Walaupun kami boleh secara aktif menyerahkan tugasan tak segerak kepada utas dalam kumpulan utas untuk dilaksanakan, selepas pelaksanaan tugas tak segerak selesai, utas utama tidak boleh dimaklumkan sama ada tugas itu telah selesai atau tidak melalui kaedah dapatkan.

Kepingan masa hadapan diasingkan antara satu sama lain: Kadangkala selepas tugas tak segerak yang telah lama dijalankan, anda ingin menggunakan hasil yang dikembalikan olehnya untuk melaksanakan operasi selanjutnya, dan hubungan antara kedua-dua Perhubungan memerlukan pembangun program mengikat dan menetapkannya secara manual tidak boleh membentuk aliran tugas (talian paip Setiap Masa Depan diasingkan, jadi terdapat CompletableFuture boleh menggabungkan berbilang Futures disambungkan secara bersiri .

Futrue tidak mempunyai mekanisme pengendalian ralat yang baik:

Sehingga kini, jika pengecualian berlaku semasa pelaksanaan tugas tak segerak, pemanggil tidak boleh mengesannya secara pasif untuk mengetahui sama ada kaedah get tugas tak segerak dilaksanakan Ralat telah berlaku, dan pertimbangan dan pemprosesan selanjutnya diperlukan.

3. . . Ini ialah mod menggunakan kumpulan benang tersuai. Tidak disyorkan untuk menggunakan @Async secara langsung untuk melaksanakan tak segerak.

5. Acara Aplikasi Musim Bunga tidak segerak

(1)定义事件

public class AsyncSendEmailEvent extends ApplicationEvent {
    /**
     * 邮箱
     **/
    private String email;
   /**
     * 主题
     **/
    private String subject;
    /**
     * 内容
     **/
    private String content;
  
    /**
     * 接收者
     **/
    private String targetUserId;

}

(2)定义事件处理器

@Slf4j
@Component
public class AsyncSendEmailEventHandler implements ApplicationListener<AsyncSendEmailEvent> {

    @Autowired
    private IMessageHandler mesageHandler;
    
    @Async("taskExecutor")
    @Override
    public void onApplicationEvent(AsyncSendEmailEvent event) {
        if (event == null) {
            return;
        }

        String email = event.getEmail();
        String subject = event.getSubject();
        String content = event.getContent();
        String targetUserId = event.getTargetUserId();
        mesageHandler.sendsendEmailSms(email, subject, content, targerUserId);
      }
}

另外,可能有些时候采用ApplicationEvent实现异步的使用,当程序出现异常错误的时候,需要考虑补偿机制,那么这时候可以结合Spring Retry重试来帮助我们避免这种异常造成数据不一致问题。

6、消息队列

(1)回调事件消息生产者

@Slf4j
@Component
public class CallbackProducer {

    @Autowired
    AmqpTemplate amqpTemplate;

    public void sendCallbackMessage(CallbackDTO allbackDTO, final long delayTimes) {

        log.info("生产者发送消息,callbackDTO,{}", callbackDTO);

        amqpTemplate.convertAndSend(CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getExchange(), CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getRoutingKey(), JsonMapper.getInstance().toJson(genseeCallbackDTO), new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //给消息设置延迟毫秒值,通过给消息设置x-delay头来设置消息从交换机发送到队列的延迟时间
                message.getMessageProperties().setHeader("x-delay", delayTimes);
                message.getMessageProperties().setCorrelationId(callbackDTO.getSdkId());
                return message;
            }
        });
    }
}

(2)回调事件消息消费者

@Slf4j
@Component
@RabbitListener(queues = "message.callback", containerFactory = "rabbitListenerContainerFactory")
public class CallbackConsumer {

    @Autowired
    private IGlobalUserService globalUserService;

    @RabbitHandler
    public void handle(String json, Channel channel, @Headers Map<String, Object> map) throws Exception {

        if (map.get("error") != null) {
            //否认消息
            channel.basicNack((Long) map.get(AmqpHeaders.DELIVERY_TAG), false, true);
            return;
        }

        try {
        
            CallbackDTO callbackDTO = JsonMapper.getInstance().fromJson(json, CallbackDTO.class);
            //执行业务逻辑
            globalUserService.execute(callbackDTO);
            //消息消息成功手动确认,对应消息确认模式acknowledge-mode: manual
            channel.basicAck((Long) map.get(AmqpHeaders.DELIVERY_TAG), false);

        } catch (Exception e) {
            log.error("回调失败 -> {}", e);
        }
    }
}

7、ThreadUtil异步工具类

@Slf4j
public class ThreadUtils {

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            ThreadUtil.execAsync(() -> {
                ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
                int number = threadLocalRandom.nextInt(20) + 1;
                System.out.println(number);
            });
            log.info("当前第:" + i + "个线程");
        }

        log.info("task finish!");
    }
}

8、Guava异步

Guava的ListenableFuture顾名思义就是可以监听的Future,是对java原生Future的扩展增强。我们知道Future表示一个异步计算任务,当任务完成时可以得到计算结果。如果我们希望一旦计算完成就拿到结果展示给用户或者做另外的计算,就必须使用另一个线程不断的查询计算状态。这样做,代码复杂,而且效率低下。使用「Guava ListenableFuture」可以帮我们检测Future是否完成了,不需要再通过get()方法苦苦等待异步的计算结果,如果完成就自动调用回调函数,这样可以减少并发程序的复杂度。

ListenableFuture是一个接口,它从jdk的Future接口继承,添加了void addListener(Runnable listener, Executor executor)方法。

我们看下如何使用ListenableFuture。首先需要定义ListenableFuture的实例:

ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
        final ListenableFuture<Integer> listenableFuture = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.info("callable execute...")
                TimeUnit.SECONDS.sleep(1);
                return 1;
            }
        });

首先,通过MoreExecutors类的静态方法listeningDecorator方法初始化一个ListeningExecutorService的方法,然后,使用此实例的submit方法即可初始化ListenableFuture对象。

ListenableFuture要做的工作,在Callable接口的实现类中定义,这里只是休眠了1秒钟然后返回一个数字1,有了ListenableFuture实例,可以执行此Future并执行Future完成之后的回调函数。

Futures.addCallback(listenableFuture, new FutureCallback<Integer>() {
    @Override
    public void onSuccess(Integer result) {
        //成功执行...
        System.out.println("Get listenable future&#39;s result with callback " + result);
    }

    @Override
    public void onFailure(Throwable t) {
        //异常情况处理...
        t.printStackTrace();
    }
});

那么,以上就是本期介绍的实现异步的8种方式了。

Atas ialah kandungan terperinci Beberapa kaedah pengaturcaraan tak segerak, berapa ramai yang anda tahu?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:Java后端技术全栈. Jika ada pelanggaran, sila hubungi admin@php.cn Padam