我很难理解如何在两个线程上同步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
非常感谢任何帮助
答案 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
。