java多线程初学者问题

时间:2014-06-19 06:24:57

标签: java multithreading

我正在尝试学习多线程编程,并且我对必须采取的方法有一些疑问 所以,在我的具体情况下,我想构建一个重命名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();

我该怎么办?

3 个答案:

答案 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)

更典型和可扩展的方法是以下之一:

  • 创建N个线程的集合(可能是数组或列表)以执行工作
  • 使用线程池,例如来自Executors.newFixedThreadPool(N)

您可能还希望使用生产者消费者模式,其中线程从公共任务池中提取。这允许工作的自然平衡 - 而不是基本上硬编码一个线程处理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;
}

}