首页 > 其他分享 >【工具】简陋的异步转同步队列

【工具】简陋的异步转同步队列

时间:2022-12-10 11:13:13浏览次数:45  
标签:异步 runnable 队列 简陋 void new mThread public SimpleAsyncToSyncQueue

前言

最近碰到一个场景,在开发一个需求的过程中将系统的接口封装了一层,但是系统接口全部是以异步的形式回调的,这导致两个问题:

  1. 外部操作传入封装类的时候,上一次的操作还未完成,导致封装内部对象状态并不正确
  2. 某些场景下外部操作会并发地创建,因此需要一个队列来完成有序执行和生产者阻塞

基于以上两点,需要实现一个简易的队列,满足开发中的需要

过程分析

首先需要创建一个队列,用来保存 Runnable 任务,在这里选择的是 LinkedBlockingQueue 队列,当然也可以使用 ArrayBlockingQueue 队列限制任务数,或者在 SimpleAsyncToSyncQueue 内部自己实现

SimpleAsyncToSyncQueue 本身作为任务分配者,单独运行在一个线程内,此时生产者可能在不同线程中同时向 LinkedBlockingQueue 传入任务,这部分存在多线程操作的情况,好在 LinkedBlockingQueue 内部已经实现

因为生产者或消费者可能处于不同的线程中,因此实际控制的逻辑均放在 SimpleAsyncToSyncQueue 中,此处用到的是 Java 提供的线程锁工具 LockSupport,在运行了 Runnable 之后,SimpleAsyncToSyncQueue 会调用 LockSupport.park() 将自身线程锁定,等待任务完成

Runnable 运行完成并不一定是任务完成,真正的任务可能在多个调用链之后才结束,因此在任务的调用链中无论是正常结束、非法值返回又或者抛出异常,都需要调用 unlock() 方法解锁SimpleAsyncToSyncQueue 线程

结论

package xyz.slkagura.thread;

import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.LockSupport;

public class SimpleAsyncToSyncQueue {
    private static final String SIMPLE_ASYNC_TO_SYNC_QUEUE_TAG = SimpleAsyncToSyncQueue.class.getSimpleName();
    
    private final LinkedBlockingQueue<Runnable> mQueue = new LinkedBlockingQueue<>();
    
    private final Thread mThread = new Thread(this::run);
    
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    
    private boolean mIsAuto = false;
    
    public void start() {
        if (!mThread.isAlive()) {
            mThread.start();
        }
    }
    
    public void stop() {
        if (mThread.isAlive() && !mThread.isInterrupted()) {
            mThread.interrupt();
        }
    }
    
    public void setAuto(boolean isAuto) {
        mIsAuto = isAuto;
    }
    
    private void run() {
        try {
            if (mIsAuto) {
                mQueue.take().run();
            }
            while (!mThread.isInterrupted()) {
                LockSupport.park();
                Runnable runnable = mQueue.take();
                runnable.run();
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    
    public void offer(@NonNull Runnable runnable, boolean isMain) {
        if (isMain) {
            mQueue.offer(() -> {
                mHandler.post(runnable);
            });
        } else {
            mQueue.offer(runnable);
        }
    }
    
    public void offer(@NonNull Runnable runnable) {
        offer(runnable, false);
    }
    
    public void unlock() {
        LockSupport.unpark(mThread);
    }
    
    public static void test() {
        SimpleAsyncToSyncQueue consumer = new SimpleAsyncToSyncQueue();
        consumer.setAuto(true);
        consumer.start();
        for (int i = 0; i < 100; i++) {
            final int id = i;
            boolean isSync = Math.random() < 0.9D;
            String groupId = isSync ? "group-1" : "group-2";
            consumer.offer(() -> {
                Log.d(SIMPLE_ASYNC_TO_SYNC_QUEUE_TAG, "task: ", id, " group: ", groupId, " sync: ", String.valueOf(isSync), " start: ", System.nanoTime());
                new Thread(() -> {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    consumer.unlock();
                    Log.d(SIMPLE_ASYNC_TO_SYNC_QUEUE_TAG, "task: ", id, " group: ", groupId, " sync: ", String.valueOf(isSync), " unlock: ", System.nanoTime());
                }).start();
                Log.d(SIMPLE_ASYNC_TO_SYNC_QUEUE_TAG, "task: ", id, " group: ", groupId, " sync: ", String.valueOf(isSync), " end: ", System.nanoTime());
            });
        }
    }
}

标签:异步,runnable,队列,简陋,void,new,mThread,public,SimpleAsyncToSyncQueue
From: https://www.cnblogs.com/slkagura/p/16971154.html

相关文章

  • [转]React hooks useEffect中如何使用异步函数(即如何使用async/await)
    1.useEffect的回调参数返回的是一个清除副作用的clean-up函数。因此无法返回Promise,更无法使用async/await2.如何让useEffect支持async/await2.1、方法一(推荐):useEf......
  • python初步了解队列
    python初步了解队列队列是一种先入先出的数据结构单纯用列表来实现队列运用pop()函数,进行出队效率很低,因为在列表开头删除元素需要将其他元素往前移动一位.所以一般用......
  • 单调栈与单调队列
    单调栈是栈中数据具有单调性的一种数据结构,用来求以某个值为最值的最大区间等问题模板(c++):intn,in;intstack[1010];inttop;func(){for(int......
  • js中如何顺序执行异步任务
    在js中,任务可分为两种,同步任务和异步任务。(1)同步任务又叫非耗时任务,指的是在主线程排队执行的那些任务只有前一个任务执行完毕,才能执行后一个任务(2)异步任务又......
  • JavaScript入门⑨-异步编程●异世界之旅
    JavaScript入门系列目录JavaScript入门①-基础知识筑基JavaScript入门②-函数(1)基础{浅出}JavaScript入门③-函数(2)原理{深入}执行上下文JavaScript入门④-万物皆......
  • 栈和队列--生成窗口最大值数组
    题目:有一个整型数组arr和一个大小为w的窗口从数组最左边滑到最右边,窗口每次向右边滑一个位置例如:数组为【4,3,5,4,3,3,6,7】,窗口大小为三时如果数组长度为n,窗口大......
  • 关于异步在操作系统层面的支持
    DMA(直接内存访问)拥有DMA功能的硬件在和内存进行数据交换的时候可以不消耗CPU资源。只要CPU在发起数据传输时发送一个指令,硬件就开始自己和内存交换数据,在传输完成之后......
  • Axios异步通信
    什么是Axios?Axios是一个开源的可以用在浏览器端和NodeJS的异步通信框架,主要作用就是实现AJAX异步通信,其功能特点如下:··从浏览器中创建XMLHttpRequests··从node......
  • java 异步总线guava eventBus
    参见: https://blog.csdn.net/winy_lm/article/details/88076968?spm=1001.2101.3001.6650.14&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EESLANDI......
  • Kafka技术专题之「性能调优篇」消息队列服务端出现内存溢出OOM以及相关性能调优实战分
    内存问题本篇文章介绍Kafka处理大文件出现内存溢出java.lang.OutOfMemoryError:Directbuffermemory,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮......