通过两个线程同步ArrayList

时间:2016-04-21 18:46:03

标签: java multithreading arraylist

我很难理解如何在两个线程上同步ArrayList。基本上,我希望一个线程将对象附加到列表中,另一个线程同时从该列表中读取。

以下是部署线程的类:

public class Main {
    public static ArrayList<Good> goodList = new ArrayList();
    public static void main(String[] args) {
        Thread thread1 = new Thread(new GoodCreator());
        Thread thread2 = new Thread(new WeightCounter());
        thread1.start();
        thread2.start();
    }
}

然后是两个Runnable类:

这个从文本文件中读取两行值并添加新对象。

public class GoodCreator implements Runnable{
    private ArrayList<Good> goodList = Main.goodList;
    private static Scanner scan;
    @Override
    public void run() {
        System.out.println("Thread 1 started");
        int objCount = 0;
        try {
            scan = new Scanner(new File(System.getProperty("user.home") + "//Goods.txt"));
        } catch (FileNotFoundException e) {
            System.out.println("File not found!");
            e.printStackTrace();
        }
        while(scan.hasNextLine()){
            String line = scan.nextLine();
            String[] words = line.split("\\s+");
            synchronized(goodList){
                goodList.add(new Good(Integer.parseInt(words[0]), Integer.parseInt(words[1])));
                objCount++;
            }
            if(objCount % 200 == 0) System.out.println("created " + objCount + " objects");
        }
    }

}

这遍历了arraylist并且应该总结其中一个字段。

public class WeightCounter implements Runnable{
    private ArrayList<Good> goodList = Main.goodList;
    @Override
    public void run() {
        System.out.println("Thread 2 started");
        int weightSum = 0;
        synchronized(goodList){
            for(Good g : goodList){
                weightSum += g.getWeight();
            }
        }
        System.out.println(weightSum);

    }

}

无论输入如何,weightSum都不会增加并保持0

Thread 1 started
Thread 2 started
0

非常感谢任何帮助

3 个答案:

答案 0 :(得分:2)

您正在运行两个独立运行的线程。这些线程可以以任何顺序运行,如果一个停止,例如要从文件中读取,另一个线程并不认为必须等待它。

简而言之,第二个线程在第一个线程向列表添加任何内容之前完成。

没有好的解决方法,因为这不是一个很好的例子,说明为什么你会使用多个线程,但要获得结果,你可以做的就是这个。

HTTPRequest.assertNoRequestsHaveBeenMade();

这将打印10次,相隔0.1秒。根据文件加载的时间长短,您将能够看到到目前为止加载的内容的总和。

答案 1 :(得分:1)

这就是生产者 - 消费者任务。你可以用arraylist来做,但老实说,这不是解决这个问题的正确方法。

幸运的是,Java为我们提供了一些集合,即BlockingQueue集合,这些集合是专门为此而设计的;

//the collection with the stuff in it
static BlockingQueue<Object> items = new BlockingQueue<Object>();
//(there are a few different types of blocking queues, check javadocs.
//you would want Linked or Array blocking queue

//what happens on the reader thread
public void producer()
{
    //read the data into the collection
    for (all the data in the file)
    {
       //add the next item
       items.put(/* next item from file or w/e */);

       //stop if necessary
       if (atEndOfFile) stillReadingData = false;

       //etc
    }
}

现在你需要从队列中读取数据 - 幸运的是,这很容易;

//what happens on the other threads
public void consumer()
{


   //keep this thread alive so long as there is data to process
   //or so long as there might be more data to process
   while (stillReadingData || !items.isEmpty())
   {
       //get the next item from the list
       //while the list is empty, we basically sleep for "timeout" timeunits,
       //then the while-loop would repeat, and so on
       Object o = items.poll(long timeout, int units);
       if (o != null) //process it
   }
}

通过这种方式,您可以使用生产者线程持续向队列中添加项目,并且一旦消费者线程空闲,这些项目就会被处理(这种方法可以很好地扩展到许多消费者线程)。如果你仍然需要一个项目的集合,那么你应该制作第二个集合,并在处理完毕后将它们添加到集合中。

作为旁注,您可能仍需要同步处理项目时发生的操作。例如,您需要在“weightSum”上同步增量(或者使用AtomicInteger)。

答案 2 :(得分:0)

WeightCounter课程中尝试此更改。

public class WeightCounter implements Runnable{
   private ArrayList<Good> goodList = Main.goodList;
   @Override
   public void run() {
      System.out.println("Thread 2 started");
      int weightSum = 0;
      while(goodList.isEmpty()) {
        Thread.sleep(1000);
      }
      synchronized(goodList){
        for(Good g : goodList){
            weightSum += g.getWeight();
        }
     }
     System.out.println(weightSum);
   }
}

此更改将导致WeightCounter线程等待另一个线程在尝试读取数据之前用数据填充goodList