策略设计
- 线程池的使用:使用线程池来管理并发任务,线程池的大小根据系统资源(如 CPU 核心数、可用内存等)进行合理配置。例如,根据 CPU 核心数来确定线程池的大小,公式可以是
CPU核心数 * 2
,这样既能充分利用 CPU 资源,又不会因为线程过多导致上下文切换开销过大。
- 任务分块:将长字符串数组分成若干个小块,每个小块分配给线程池中的一个线程进行处理。块的大小可以根据字符串长度和系统资源动态调整。例如,对于非常长的字符串,可以划分较大的块,以减少任务调度开销;对于较短的字符串,可以划分较小的块,以提高并发度。
- 结果合并:每个线程处理完自己负责的块后,将结果返回。使用一个数据结构(如数组或链表)来收集所有线程的处理结果,并按照原始顺序合并,以保证处理结果的正确性。
代码实现(以 Java 为例)
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class StringProcessor {
private static final int THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
private static final int BLOCK_SIZE = 1000;
public static String processString(String input, Callable<Character> processor) {
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
List<Future<Character[]>> futures = new ArrayList<>();
char[] charArray = input.toCharArray();
for (int i = 0; i < charArray.length; i += BLOCK_SIZE) {
int end = Math.min(i + BLOCK_SIZE, charArray.length);
char[] block = new char[end - i];
System.arraycopy(charArray, i, block, 0, block.length);
Callable<Character[]> task = () -> {
Character[] processedBlock = new Character[block.length];
for (int j = 0; j < block.length; j++) {
processedBlock[j] = processor.call(block[j]);
}
return processedBlock;
};
futures.add(executorService.submit(task));
}
executorService.shutdown();
StringBuilder result = new StringBuilder();
try {
for (Future<Character[]> future : futures) {
Character[] processedBlock = future.get();
for (Character c : processedBlock) {
result.append(c);
}
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return result.toString();
}
public static void main(String[] args) {
String input = "a very long string...";
Callable<Character> processor = (c) -> {
// 模拟复杂的加密运算
return (char) (c + 1);
};
String processed = processString(input, processor);
System.out.println(processed);
}
}
优势和局限性
- 不同长度字符串
- 优势:对于长字符串,通过合理分块和线程池管理,可以充分利用系统资源,提高处理效率。对于短字符串,虽然线程池可能存在一定的开销,但由于块大小可调整,依然可以保证一定的并发度,不会因为线程过多而导致资源浪费。
- 局限性:如果字符串非常短,线程池的创建和任务调度开销可能相对较大,此时并发处理的优势不明显。
- 不同复杂度的字符处理函数
- 优势:对于复杂的字符处理函数,通过并发处理可以充分利用多核 CPU 的优势,加快整体处理速度。线程池的资源管理机制可以避免因任务过多导致系统资源耗尽。
- 局限性:如果字符处理函数非常简单,并发处理带来的上下文切换开销可能大于并行处理带来的性能提升,导致整体效率下降。同时,如果处理函数涉及共享资源的访问,需要额外的同步机制,可能会降低并发度。