
在处理大量数据时,例如计算一个字符串列表内所有字符串两两之间的相似度,如果采用单线程顺序执行,效率会非常低下。而如果采用多线程,但任务分解不合理或同步机制使用不当,也可能无法达到预期的并行效果,甚至引入新的问题。本教程将指导您如何使用java的executorservice,以一种标准且高效的方式实现这一目标。
在原始场景中,用户尝试通过创建一个Runnable实例,并在其中使用synchronized (LOCK)来同步对整个列表的相似度计算过程。这种做法存在以下几个问题:
正确的做法是将大任务分解成多个小任务,每个小任务独立完成一部分工作,然后由线程池来调度这些小任务的并行执行。
Java的java.util.concurrent.ExecutorService提供了一种高级的并发API,用于管理线程池和提交任务。通过它,我们可以将计算字符串相似度的任务分解为针对每个字符串的独立子任务,然后由线程池中的线程并行执行。
以下是使用ExecutorService实现字符串相似度并行计算的示例代码:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 假设有一个Solution类提供findSimilarityRatio方法
class Solution {
public double findSimilarityRatio(String s1, String s2) {
// 模拟耗时的相似度计算
try {
Thread.sleep(10); // 模拟计算耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 实际的相似度计算逻辑
return (double) Math.min(s1.length(), s2.length()) / Math.max(s1.length(), s2.length());
}
}
// 假设listExe提供获取字符串列表的方法
class ListExecutor {
public List<String> getStringList() {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("apricot");
list.add("banana");
list.add("bandana");
list.add("orange");
list.add("grape");
return list;
}
}
public class MultiThreadSimilarityCalculator {
// solution对象,假设它是线程安全的或我们会在需要时同步
private static final Solution solution = new Solution();
private static final ListExecutor listExe = new ListExecutor();
public static void main(String[] args) {
// 创建一个固定大小为10的线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<String> stringList = listExe.getStringList();
// 遍历列表中的每个字符串,为每个字符串创建一个任务
for (String str : stringList) {
// 将任务提交给线程池
pool.submit(new SimilarityRunnable(str, stringList));
}
// 所有任务提交完毕后,关闭线程池。
// shutdown()方法会阻止新的任务提交,并等待所有已提交任务完成。
pool.shutdown();
System.out.println("所有任务已提交,线程池正在关闭...");
}
/**
* SimilarityRunnable 类封装了计算一个字符串与列表中所有其他字符串相似度的任务。
*/
private static class SimilarityRunnable implements Runnable {
private final String str; // 当前要比较的字符串
private final List<String> stringList; // 整个字符串列表
public SimilarityRunnable(String str, List<String> stringList) {
this.str = str;
this.stringList = stringList;
}
@Override
public void run() {
for (String listStr : stringList) {
// 避免自身与自身比较,这里使用 == 判断引用是否相同,
// 如果字符串内容相同但引用不同,仍会比较。
// 如果需要严格避免内容相同的字符串比较,需要使用 equals()。
if (listStr == str) {
continue;
}
// 如果 solution 对象不是线程安全的,需要在这里进行同步。
// 例如:synchronized (solution) { ... }
System.out.println(Thread.currentThread().getName()
+ ": 字符串 '" + str + "' 与 '" + listStr + "' 的相似度是 "
+ solution.findSimilarityRatio(str, listStr));
}
}
}
}main 方法:
SimilarityRunnable 类:
pool.shutdown();
try {
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { // 等待最多60秒
pool.shutdownNow(); // 如果超时,强制关闭
}
} catch (InterruptedException ex) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}通过本教程,我们学习了如何利用Java的ExecutorService和线程池机制,将字符串相似度计算任务分解为可并行执行的子任务。这种模式不仅能够有效利用多核处理器的性能,提高应用程序的响应速度和吞吐量,而且通过线程池的管理,避免了手动创建和销毁线程的开销,使并发编程更加简洁和高效。理解并正确应用这种任务分解和线程池管理模式,是进行高效并发编程的关键。
以上就是使用ExecutorService高效并行计算字符串相似度的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号