多線程競爭執行,誰先跑完用誰?

       做性能優化時,遇到一些跨不過的坎,如果能夠繞過問題本身來解決該問題也是不錯的選擇。本人在做 MongoDB 聯合查詢時,遇到了性能瓶頸,使用 MongoDB 內部聯合查詢時 Mongo竟然不會使用索引,效率低下,不得采用數據庫外部聯合查詢來解決。那麼,問題來了,兩種方式優勢與劣勢同樣明顯,若要同時運用兩種查詢方式的優勢,避其短處,本人採用了競爭式查詢。在不同的場景下,兩者同時執行,誰最快執行完成,就用誰,保證執行時間最短。這種方式也是典型的用資源或者空間換時間的做法了。

       多線程競爭代碼如下:

package com.zhaoxj_2017.tools;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

public class CompetitiveExecutor {

    @Test
    public void execute() throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();

        long startTime = System.currentTimeMillis();

        List<FutureTask<Integer>> tasks = new ArrayList<>();
        Signal signal = new Signal();
        CompetitiveCallable callable1 = createCompetitiveCallable(signal);
        CompetitiveCallable callable2 = createCompetitiveCallable(signal);
        CompetitiveCallable callable3 = createCompetitiveCallable(signal);
        CompetitiveCallable callable4 = createCompetitiveCallable(signal);

        FutureTask<Integer> task1 = new FutureTask<>(callable1);
        FutureTask<Integer> task2 = new FutureTask<>(callable2);
        FutureTask<Integer> task3 = new FutureTask<>(callable3);
        FutureTask<Integer> task4 = new FutureTask<>(callable4);

        tasks.add(task1);
        tasks.add(task2);
        tasks.add(task3);
        tasks.add(task4);

        callable1.setCompetitors(tasks);
        callable1.setCurrentTask(task1);
        callable2.setCompetitors(tasks);
        callable2.setCurrentTask(task2);
        callable3.setCompetitors(tasks);
        callable3.setCurrentTask(task3);
        callable4.setCompetitors(tasks);
        callable4.setCurrentTask(task4);

        executorService.submit(task1);
        executorService.submit(task2);
        executorService.submit(task3);
        executorService.submit(task4);

        Integer result = -1;

        for (FutureTask<Integer> task : tasks) {
            try {
                result = task.get();
            } catch (Exception e) {
                continue;
            }

            break;
        }

        System.out.println("Result is : " + result);
        System.out.println("Total execution time is " + (System.currentTimeMillis() - startTime));

        TimeUnit.SECONDS.sleep(10);
        executorService.shutdown();
    }

    private CompetitiveCallable createCompetitiveCallable(Signal signal) {
        return new CompetitiveCallable(new Random().nextInt(5) + 2, signal);
    }


    class Signal {
        private boolean state = true;

        synchronized boolean get() {
            if(state) {
                state = false;
                return true;
            }
            return false;
        }
    }

    class CompetitiveCallable implements Callable<Integer> {
        private List<FutureTask<Integer>> competitors = Collections.emptyList();
        private FutureTask currentTask;
        private Signal signal;

        private int randomResource;
        CompetitiveCallable(int randomResource, Signal signal) {
            this.randomResource = randomResource;
            this.signal = signal;
        }
        void setCompetitors(List<FutureTask<Integer>> competitors) {
            this.competitors = new ArrayList<>(competitors);
        }

        void setCurrentTask(FutureTask currentTask) {
            this.currentTask = currentTask;
        }

        @Override
        public Integer call() throws Exception {
            System.out.println(Thread.currentThread().getName() + ": running, need cost " + randomResource);
            TimeUnit.SECONDS.sleep(randomResource);
            System.out.println(Thread.currentThread().getName() + ": completing, and cost " + randomResource);

            if(signal.get()) {
                System.out.println(Thread.currentThread().getName() + ": Obtain final execution rights.");
                competitors.remove(currentTask);
                competitors.forEach(futureTask -> {
                    try {
                        futureTask.cancel(true);
                    } catch (Exception e) {
                        // Ignore.
                    }
                });
            }
            return randomResource;
        }
    }
}

       該方式的執行時間就是執行最快的線程執行所需要的時間,與此同時,未執行完成的線程也會被取消執行。

       通過多種方式競爭,充分利用各種算法的優點,算是勉強解決了本人所遇問題,但是從根本上解決 MongoDB 聯合查詢的性能問題,仍需要 Mongo 官方繼續優化聯合查詢性能。

       如果大家有更好的解決方案,歡迎交流。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章