Java 使用线程并发散列(sha1)多个文件
我有N个大文件(不少于2.5亿个)要散列。这些文件位于P物理驱动器上 我想用最多K个活动线程同时散列它们,但我不能在每个物理驱动器上散列超过M个文件,因为这会减慢整个过程(我运行了一个测试,解析了61个文件,使用8个线程比使用1个线程慢;文件几乎都在同一磁盘上) 我想知道最好的方法是什么:Java 使用线程并发散列(sha1)多个文件,java,multithreading,sha1,java-7,Java,Multithreading,Sha1,Java 7,我有N个大文件(不少于2.5亿个)要散列。这些文件位于P物理驱动器上 我想用最多K个活动线程同时散列它们,但我不能在每个物理驱动器上散列超过M个文件,因为这会减慢整个过程(我运行了一个测试,解析了61个文件,使用8个线程比使用1个线程慢;文件几乎都在同一磁盘上) 我想知道最好的方法是什么: 我可以使用Executors.newFixedThreadPool(K) 然后,我将使用计数器提交任务,以确定是否应添加新任务 我的代码是: int K = 8; int M = 1; Queue<
- 我可以使用Executors.newFixedThreadPool(K)
- 然后,我将使用计数器提交任务,以确定是否应添加新任务
int K = 8;
int M = 1;
Queue<Path> queue = null; // get the files to hash
final ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(K);
final ConcurrentMap<FileStore, Integer> counter = new ConcurrentHashMap<>();
final ConcurrentMap<FileStore, Integer> maxCounter = new ConcurrentHashMap<>();
for (FileStore store : FileSystems.getDefault().getFileStores()) {
counter.put(store, 0);
maxCounter.put(store, M);
}
List<Future<Result>> result = new ArrayList<>();
while (!queue.isEmpty()) {
final Path current = queue.poll();
final FileStore store = Files.getFileStore(current);
if (counter.get(store) < maxCounter.get(store)) {
result.add(newFixedThreadPool.submit(new Callable<Result>() {
@Override
public Entry<Path, String> call() throws Exception {
counter.put(store, counter.get(store) + 1);
String hash = null; // Hash the file
counter.put(store, counter.get(store) - 1);
return new Result(path, hash);
}
}));
} else queue.offer(current);
}
intk=8;
int M=1;
队列=null;//让文件散列
final ExecutorService newFixedThreadPool=Executors.newFixedThreadPool(K);
最终ConcurrentMap计数器=新ConcurrentHashMap();
最终ConcurrentMap maxCounter=新ConcurrentHashMap();
对于(文件存储区:FileSystems.getDefault().getFileStores()){
计数器。放置(存储,0);
最大计数器放置(存储,M);
}
列表结果=新建ArrayList();
而(!queue.isEmpty()){
最终路径当前=queue.poll();
final FileStore store=Files.getFileStore(当前);
if(counter.get(store)
抛开潜在的非线程安全操作(比如我如何使用计数器),有没有更好的方法来实现我的目标
我还认为这里的循环可能有点太多,因为它可能会占用大量的进程(几乎像一个无限循环)。如果驱动器硬件配置在编译时未知,并且可能会被更改/升级,那么很有可能会对每个驱动器使用线程池,并让线程数由用户配置。我不熟悉“newFixedThreadPool”-线程计数是一个可以在运行时更改以优化性能的属性吗?经过很长时间,我找到了一个解决方案来满足我的需要:而不是整数计数器,或
原子整数
或其他什么,我使用了一个ExecutorService
,每个提交的任务都在一个驱动器的每个文件中使用一个共享
比如:
ConcurrentMap=new ConcurrentHashMap();
Executors服务es=Executors.newFixedThreadPool(10);
对于(路径:listFile()){
final FileStore store=Files.getFileStore(路径);
final Semaphore Semaphore=map.computeIfAbsent(存储,键->新信号量(getAllocatedCredits(存储));
最终整数成本=计算成本(路径);
提交(()->{
信号量获取(成本);
试一试{
…一些工作。。。
}最后{
信号量释放(成本);
}
});
}
int getAllocatedCredits(文件存储区){return 2;}
int computeCost(路径){return 1;}
请注意Java 8的帮助,特别是在
computeIfAbsent
和submit
中,您知道哪个驱动器上有哪个文件吗?我只需要为每个驱动器分配一个线程,让它处理该驱动器上的所有文件。是的,每个驱动器一个线程可能是可行的。是的。事实上,FileStore提供了逻辑驱动器(可能是物理驱动器,也可能不是物理驱动器)。最后,我想在一个有5个驱动器的RAID的NAS上运行我的代码,这将允许更多的文件被散列。作为旁注,使用do{int count=counter.get(store);}而(!counter.replace(store,count,count+1))代码>而不是get/put(替换是concurrentmap的CompareAndSwap)我concur:每个驱动器一个可防止磁头移位。优先级队列也很好,可以防止拥塞。优先级基于队列中的时间和负文件大小:与已处理的累积大小类似-队列条目的文件大小。我简化了我的示例,但我打算让用户(我自己^^)更改最大线程数(如果超线程,则更改核心数*2)和每个磁盘的最大线程数。
ConcurrentMap<FileStore, Semaphore> map = new ConcurrentHashMap<>();
ExecutorService es = Executors.newFixedThreadPool(10);
for (Path path : listFile()) {
final FileStore store = Files.getFileStore(path);
final Semaphore semaphore = map.computeIfAbsent(store, key -> new Semaphore(getAllocatedCredits(store)));
final int cost = computeCost(path);
es.submit(() -> {
semaphore.acquire(cost);
try {
... some work ...
} finally {
semaphore.release(cost);
}
});
}
int getAllocatedCredits(FileStore store) {return 2;}
int computeCost(Path path) {return 1;}