<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Java批量更新太慢?多線程+List分段完美解決!

    VSole2022-07-12 17:10:12

    寫在前面

    相信不少開發者在遇到項目對數據進行批量操作的時候,都會有不少的煩惱,尤其是針對數據量極大的情況下,效率問題就直接提上了菜板。

    因此,開多線程來執行批量任務是十分重要的一種批量操作思路,其實這種思路實現起來也十分簡單,就拿批量更新的操作舉例。

    整體流程圖如下:

    步驟

    步驟如下:

    • 獲取需要進行批量更新的大集合 A,對大集合進行拆分操作,分成 N 個小集合 A-1 ~ A-N 。
    • 開啟線程池,針對集合的大小進行調參,對小集合進行批量更新操作。
    • 對流程進行控制,控制線程執行順序。

    按照指定大小拆分集合的工具類:

    import com.google.common.collect.Lists;
    import org.apache.commons.collections.CollectionUtils;
    import java.util.List;
    /**
     * 拆分結合工具類
     *
     * @author shiwen
     * @date 2020/12/27
     */
    public class SplitListUtils {
        /**
         * 拆分集合
         *
         * @param <T> 泛型對象
         * @param resList 需要拆分的集合
         * @param subListLength 每個子集合的元素個數
         * @return 返回拆分后的各個集合組成的列表
         * 代碼里面用到了guava和common的結合工具類
         **/
        public static  List<List> split(List resList, int subListLength) {
            if (CollectionUtils.isEmpty(resList) || subListLength <= 0) {
                return Lists.newArrayList();
            }
            List<List> ret = Lists.newArrayList();
            int size = resList.size();
            if (size <= subListLength) {
                // 數據量不足 subListLength 指定的大小
                ret.add(resList);
            } else {
                int pre = size / subListLength;
                int last = size % subListLength;
                // 前面pre個集合,每個大小都是 subListLength 個元素
                for (int i = 0; i < pre; i++) {
                    List itemList = Lists.newArrayList();
                    for (int j = 0; j < subListLength; j++) {
                        itemList.add(resList.get(i * subListLength + j));
                    }
                    ret.add(itemList);
                }
                // last的進行處理
                if (last > 0) {
                    List itemList = Lists.newArrayList();
                    for (int i = 0; i < last; i++) {
                        itemList.add(resList.get(pre * subListLength + i));
                    }
                    ret.add(itemList);
                }
            }
            return ret;
        }
        // 運行代碼
        public static void main(String[] args) {
            List<String> list = Lists.newArrayList();
            int size = 1099;
            for (int i = 0; i < size; i++) {
                list.add("hello-" + i);
            }
            // 大集合里面包含多個小集合
            List<List<String>> temps = split(list, 100);
            int j = 0;
            // 對大集合里面的每一個小集合進行操作
            for (List<String> obj : temps) {
                System.out.println(String.format("row:%s -> size:%s,data:%s", ++j, obj.size(), obj));
            }
        }
    }
    

    開啟異步執行任務的線程池:

    public void threadMethod() {
        List updateList = new ArrayList();
        // 初始化線程池, 參數一定要一定要一定要調好!!!!
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(20, 50,
                4, TimeUnit.SECONDS, new ArrayBlockingQueue(10), new ThreadPoolExecutor.AbortPolicy());
        // 大集合拆分成N個小集合, 這里集合的size可以稍微小一些(這里我用100剛剛好), 以保證多線程異步執行, 過大容易回到單線程
        List splitNList = SplitListUtils.split(totalList, 100);
        // 記錄單個任務的執行次數
        CountDownLatch countDownLatch = new CountDownLatch(splitNList.size());
        // 對拆分的集合進行批量處理, 先拆分的集合, 再多線程執行
        for (List singleList : splitNList) {
            // 線程池執行
            threadPool.execute(new Thread(new Runnable(){
                @Override
                public void run() {
                    for (Entity yangshiwen : singleList) {
                        // 將每一個對象進行數據封裝, 并添加到一個用于存儲更新數據的list
                        // ......
                    }
                }
            }));
            // 任務個數 - 1, 直至為0時喚醒await()
            countDownLatch.countDown();
        }
        try {
            // 讓當前線程處于阻塞狀態,直到鎖存器計數為零
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new BusinessLogException(ResponseEnum.FAIL);
        }
        // 通過mybatis的批量插入的方式來進行數據的插入, 這一步還是要做判空
        if (GeneralUtil.listNotNull(updateList)) {
            batchUpdateEntity(updateList);
            LogUtil.info("xxxxxxxxxxxxxxx");
        }
    }
    

    寫在最后

    多線程是 Java 的一個難點,但是它也很有趣,聽說玩得溜得起飛的人,人生都開啟多線程模式了…

    多線程
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    前兩天做了一個導入的功能,導入開始的時候非常慢,導入2w條數據要1分多鐘,后來一點一點的優化,從直接把list懟進Mysql中,到分配把list導入Mysql中,到多線程把list導入Mysql中。 時間是一點一點的變少了。非常的爽,最后變成了10s以內。 下面就展示一下過程。
    聲明:文章中涉及的程序(方法)可能帶有攻擊性,僅供安全研究與教學之用,讀者將其信息做其他用途,由用戶承擔全部
    Shreder是一款功能強大的多線程SSH協議密碼爆破工具,廣大研究人員可以使用Shreder對SSH協議的安全性進行探究。
    任務的狀態保存及再加載, 這段過程就叫做上下文切換。上下文切換會導致額外的開銷,常常表現為高并發執行時速度會慢串行,因此減少上下文切換次數便可以提高多線程程序的運行效率。在這種機制下,一個線程的堵塞不會導致整個進程堵塞。當CPU接收到中斷請求時,會在正在運行的程序和發起中斷請求的程序之間進行一次上下文切換。高并發,低耗時的情況,建議少線程。
    多線程是Java的一個難點,但是它也很有趣,聽說玩得溜得起飛的人,人生都開啟多線程模式了…
    常見服務弱口令工具
    2022-12-05 09:47:22
    常見服務弱口令工具
    -o, -output string output file to write found results
    介紹一個好用的web信息收集工具 其功能包括: 子域名收集 多線程子域名爆破 指紋信息收集 備案信息收集 批量子域名收集和批量子域名爆破 是一款挖掘SRC的實用小工具
    Arjun介紹Arjun是一款HTTP參數挖掘套件。Arjun功能介紹多線程徹底檢測支持GET / POST / JSON方法常規掃描僅需30秒基于正則表達式的啟發式掃描提供了25980個可掃描的參數名只想目標發送30-5個請求即可完成任務注意:當前版本的Arjun不支持Python < 3.4的環境。類似的,用戶可以使用–post來查找POST請求。添加HTTP Header用戶可以使用“–headers”選項來開啟交互式命令行,然后輸入需要設置的header。
    Java并發隊列與容器
    2022-07-29 10:03:36
    所謂“阻塞”是指在某些情況下線程被掛起,當滿足一定條件時會被自動喚醒,可以通過API進行控制。生產和消費數據時,直接將枚舉對象插入或刪除,不會產生或銷毀額外的對象實例。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类