我正在尝试学习多线程编程,并且我对必须采取的方法有一些疑问 所以,在我的具体情况下,我想构建一个重命名1000个文件的程序,我正在考虑创建一个worker类:
public class Worker implements Runnable {
private List<File> files ;
public Worker(List<File> f){
files = f;
}
public void run(){
// read all files from list and rename them
}
}
然后在主类中执行以下操作:
Worker w1 = new Worker(..list of 500 files...) ;
Worker w2 = new Worker(..list of the other 500 files...) ;
Thread t1 = new Thread(w1,"thread1");
Thread t2 = new Thread(w2,"thread2");
t1.start();
t2.start();
运行这个没有带来并发性问题所以我不需要同步代码,但我不确定这是否是正确的方法......?
或者我应该只创建一个Worker()实例并传递整个1000个文件列表,并注意无论有多少个线程访问该对象,都不会从列表中获取相同的文件?
即:
Worker w1 = new Worker(..list of 1000 files...) ;
Thread t1 = new Thread(w1,"thread1");
Thread t2 = new Thread(w1,"thread2");
t1.start();
t2.start();
我该怎么办?
答案 0 :(得分:4)
你说的第一种方法是正确的。您需要创建两个Worker
,因为每个工作人员都可以在不同的文件列表上工作。
Worker w1 = new Worker(..list of 500 files...) ; // First List
Worker w2 = new Worker(..list of the other 500 files...) ; // Second List
Thread t1 = new Thread(w1,"thread1");
Thread t2 = new Thread(w2,"thread2");
t1.start();
t2.start();
这里很简单,两个不同的线程,加载500个文件将同时执行。
答案 1 :(得分:1)
更典型和可扩展的方法是以下之一:
您可能还希望使用生产者消费者模式,其中线程从公共任务池中提取。这允许工作的自然平衡 - 而不是基本上硬编码一个线程处理500个任务而另一个线程处理相同的数字。
考虑一下如果所有较大的文件最终都在Thread2处理的存储桶中会发生什么?第一个线程完成/空闲,第二个线程必须完成所有繁重的工作。
生产者/消费者池方法是将所有工作(由生产者生成)转储到任务池中,然后消费者(您的工作者线程)一次咬掉小块(例如一个文件)。这种方法导致两个线程都被占用了相似的持续时间。
答案 2 :(得分:0)
在学习多线程编程时,一个重要的见解是线程不是任务。通过给线程提供要处理的项目列表的一部分,你就在那里,但下一步将带你进一步:以任何数量的线程都可以执行它的方式构造任务。为此,您必须熟悉java.util.concurrent
类。这些是帮助构建任务的有用工具。
以下示例将任务与线程分开。它使用AtomicInteger
来确保每个线程选择一个唯一的任务,并使用CountDownLatch
来了解所有工作何时完成。该示例还显示了平衡:执行更快完成任务的线程,执行更多任务。
这个例子绝不是唯一的解决方案 - 还有其他方法可以更快,更容易,更好地维护等等。
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class MultiRename implements Runnable {
public static void main(String[] args) {
final int numberOfFnames = 50;
MultiRenameParams params = new MultiRenameParams();
params.fnameList = new ArrayList<String>();
for (int i = 0; i < numberOfFnames; i++) {
params.fnameList.add("fname " + i);
}
params.fnameListIndex = new AtomicInteger();
final int numberOfThreads = 3;
params.allDone = new CountDownLatch(numberOfThreads);
ExecutorService tp = Executors.newCachedThreadPool();
System.out.println("Starting");
for (int i = 0; i < numberOfThreads; i++) {
tp.execute(new MultiRename(params, i));
}
try { params.allDone.await(); } catch (Exception e) {
e.printStackTrace();
}
tp.shutdownNow();
System.out.println("Finished");
}
private final MultiRenameParams params;
private final Random random = new Random();
// Just to show there are fast and slow tasks.
// Thread with lowest delay should get most tasks done.
private final int delay;
public MultiRename(MultiRenameParams params, int delay) {
this.params = params;
this.delay = delay;
}
@Override
public void run() {
final int maxIndex = params.fnameList.size();
int i = 0;
int count = 0;
while ((i = params.fnameListIndex.getAndIncrement()) < maxIndex) {
String fname = params.fnameList.get(i);
long sleepTimeMs = random.nextInt(10) + delay;
System.out.println(Thread.currentThread().getName() + " renaming " + fname + " for " + sleepTimeMs + " ms.");
try { Thread.sleep(sleepTimeMs); } catch (Exception e) {
e.printStackTrace();
break;
}
count++;
}
System.out.println(Thread.currentThread().getName() + " done, renamed " + count + " files.");
params.allDone.countDown();
}
static class MultiRenameParams {
List<String> fnameList;
AtomicInteger fnameListIndex;
CountDownLatch allDone;
}
}