multithreading tasks

The business requirements are as follows: accept a large amount of performance data, require multi-threaded processing of performance data, and only one of the same performance data can be processed at any time.

There are 5 classes here:

ProcessScheduler: Entry for accepting performance data and adding each piece of performance data to the queue for processing

ActionExecutor: Thread pool wrapper class

ActionQueue: task queue class, used to save tasks of the same performance, ensure thread safety, and only one task in the queue is processed at one time

ProcessAction: task class, each performance task is packaged into a task, and the business logic for data processing is in this class

ActionCommand: command class, implements Runnable interface, wraps task class for thread pool processing

 

The following code is presented in the most concise way

 

public final class ActionQueue
{
//The internal implementation of the queue, in fact, it is also possible to use the queue
    private final List<ProcessAction> runQueue = new ArrayList<ProcessAction>();
//Lock the object to ensure that it can only be stored or retrieved at the same time
    private final Object lockQueue = new Object();
//true means that the same kind of task in this queue is being processed
    public boolean IS_RUNNING = false;

    public ActionQueue(ProcessAction action)
    {
        addProcessAction(action);
    }
    public void addProcessAction(ProcessAction action)
    {
        synchronized (lockQueue)
        {
            runQueue.add(action);
        }
    }
    // If no such task is being processed at this time, get an executable action from actionQueue, otherwise return null
    public ProcessAction borrowOneCanExecuteAction()
    {
        synchronized (lockQueue)
        {
            if (!IS_RUNNING && runQueue.size()>0)
            {
                ProcessAction action = runQueue.get(0);
                runQueue.remove(0);
                IS_RUNNING = true;
                return action;
            }
            return null;
        }
    }

}

 

public final class ProcessAction
{
//Data to be processed by the task
    private RawData data;
//identification of the same data
    private String location;
    public ProcessAction(RawData data)
    {
        this.data = data;
        this.location = data.getLocation();
    }

    public void execute()
    {
        //Here is the business logic of data processing
    }

}

 

public final class ActionCommand implements Runnable
{
//The same kind of task is in the queue
    private final ActionQueue queue;
//wrapped task
    private final ProcessAction action;
    public ActionCommand(ProcessAction action, ActionQueue queue)
    {
        this.action = action;
        this.queue = queue;
    }
    public void run()
    {
//Process the internal logic of the task, set the queue IS_RUNNING flag to false after completion, and then call the method to process the next task of the same type of this task
        action.execute();
        queue.IS_RUNNING = false;
        ProcessScheduler.instance().exeActionByLocation(action.getLocation());
    }

}

 

public final class ProcessScheduler
{
// singleton
    private static ProcessScheduler inst = new ProcessScheduler();
//Thread-safe Map, the key value is the identifier of the same task, and the value value is the queue that stores the same task
    private final ConcurrentHashMap<String, ActionQueue> actionQueueMap = new ConcurrentHashMap<String, ActionQueue>();
    private final ActionExecutor actionExecutor = new ActionExecutor();
    private boolean bStart = false;
    public static ProcessScheduler instance()
    {
        return inst;
    }
    private ProcessScheduler()
    {
    }
    public void process(List<RawData> datas)
    {
        for (RawData data : datas)
        {
            String location = data.getLocation();
            ProcessAction action = new ProcessAction(data);
            addAction(location, action);
        }
    }

    private void addAction(String location, ProcessAction action)
    {
        if (location == null || action == null)
        {
            return;
        }
        if (!bStart)
        {
            start();
        }
//Get the queue to which the task belongs, add the task to the queue, and process a task in the queue
        ActionQueue queueFind = actionQueueMap.putIfAbsent(location,new ActionQueue());
        if(queueFind == null)
        {
            queueFind = actionQueueMap.putIfAbsent(location, new ActionQueue());
        }
        queueFind.addProcessAction(action);
        processQueue(queueFind);
    }

    // Get the queue according to the location from the task queue warehouse, and try to process a processable task in the queue
    public void exeActionByLocation(String location)
    {
        LOGGER.debug("Performs Process ProcessScheduler exeActionBylocation :{}",location);
        ActionQueue queueFind = actionQueueMap.get(location);
        if (queueFind == null)
        {
            return;
        }
        processQueue(queueFind);
    }

    // Try to take a task from the task queue and put it into the thread pool for processing
    private void processQueue(ActionQueue queue)
    {
        ProcessAction exeAction = queue.borrowOneCanExecuteAction();
        if (exeAction != null)
        {
            actionExecutor.executeAction(exeAction, queue);
        }
    }

   //Set the state bStart to true to ensure that the start method only executes the start method that calls actionExecutor once
    private synchronized void start()
    {
        if (bStart)
        {
            return;
        }
        bStart = true;
        this.actionExecutor.start();
    }

}

 

public final class ActionExecutor
{
    private static final int THREAD_KEEPLIVE_MINUTE = 10;
    private int corePoolSize = 10;
    private int maxCurrentNum = 45;
    private final BlockingQueue<ActionCommand> actionList;
    private ThreadPoolExecutor executor = null;
//This is not necessary, because only the start method of ProcessScheduler calls the start method here, and it is guaranteed to be called only once outside
    private boolean isStart = false;
//Alternatively not, ProcessScheduler guarantees that tasks will only be dropped after the thread pool starts
    private final Object lockStart = new Object();
    public ActionExecutor()
    {
        this.actionList = new LinkedBlockingQueue<ActionCommand>();
    }
//Process the task class, wrap it as a command class, and put it in the thread pool
    public void executeAction(ProcessAction action, ActionQueue queue)
    {
        ActionCommand command = new ActionCommand(action, queue);
        
        synchronized (lockStart) {
                executor.execute(command);
        }
    }

//Instantiate the thread pool object
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void start()
    {
        synchronized (lockStart) {
            if (isStart) {
                return;
            }
            BlockingQueue blockingQueue = new LinkedBlockingQueue();
            RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy()
            {
                public void rejectedExecution(Runnable r, ThreadPoolExecutor e)
                {
                    ActionExecutor.LOGGER.error("rejected Runnable Execution {}", r);
                }
            };
            this.executor = new ThreadPoolExecutor(corePoolSize, maxCurrentNum,
                    THREAD_KEEPLIVE_MINUTE, TimeUnit.MINUTES,
                    blockingQueue, handler);
            this.executor.setThreadFactory(new SchedulerThreadFactory());

            this.isStart = true;
        }
    }

    // The static inner class is used to wrap the new thread, the main function is to set the thread name, priority and set the new thread as a non-daemon thread
    static class SchedulerThreadFactory implements ThreadFactory
    {
        private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);

        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        public SchedulerThreadFactory()
        {
            this.namePrefix = "PerformanceProcessPool-" + POOL_NUMBER.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable r)
        {
            Thread t = new Thread(r, this.namePrefix + this.threadNumber.getAndIncrement());
            if (t.isDaemon()) {
                t.setDaemon(false);
            }
            return t;
        }

    }
}

 This is the processing of this business requirement, I have cut it out

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326226225&siteId=291194637