Java 通过两个线程同步ArrayList
我很难理解如何在两个线程上同步ArrayList。基本上,我希望一个线程将对象附加到列表中,另一个线程同时读取该列表中的对象 下面是部署线程的类:Java 通过两个线程同步ArrayList,java,multithreading,arraylist,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 Goo
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();
}
}
非常感谢您提供的任何帮助您正在运行两个独立运行的线程。这些线程可以以任何顺序运行,如果一个线程停止(例如读取文件),另一个线程不认为它必须等待它 简而言之,第二个线程在第一个线程向列表中添加任何内容之前完成 没有很好的修复方法,因为这不是一个很好的例子,说明了为什么要使用多个线程,但是为了得到结果,您可以做的就是这样
public class WeightCounter implements Runnable{
private ArrayList<Good> goodList = Main.goodList;
@Override
public void run() {
System.out.println("Thread 2 started");
for(int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
throw AssertionError(ie);
}
int weightSum = 0;
synchronized(goodList){
for (Good g : goodList)
weightSum += g.getWeight();
}
System.out.println(weightSum);
}
}
}
公共类WeightCounter实现可运行{
private ArrayList goodList=Main.goodList;
@凌驾
公开募捐{
System.out.println(“线程2已启动”);
对于(int i=0;i<10;i++){
试一试{
睡眠(100);
}捕获(中断异常ie){
抛出断言错误;
}
int-weightSum=0;
已同步(goodList){
for(Good g:goodList)
weightSum+=g.getWeight();
}
系统输出打印项次(加权和);
}
}
}
这将打印总和10次,间隔0.1秒。根据加载文件所需的时间,您将能够看到迄今为止加载的内容的总和。在
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实现可运行{
private ArrayList goodList=Main.goodList;
@凌驾
公开募捐{
System.out.println(“线程2已启动”);
int-weightSum=0;
while(goodList.isEmpty()){
睡眠(1000);
}
已同步(goodList){
for(Good g:goodList){
weightSum+=g.getWeight();
}
}
系统输出打印项次(加权和);
}
}
此更改将导致
WeightCounter
线程等待另一个线程完成向goodList
填充数据,然后再尝试从中读取数据。这就是所谓的生产者-消费者任务。你可以用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
}
}
通过这种方式,您可以使用producer线程不断地将项目添加到队列中,并且一旦消费者线程空闲,这些项目就会被处理(这种方法可以很好地扩展到许多消费者线程)。如果您仍然需要项目的集合,则应创建第二个集合,并在处理完项目后将其添加到该集合中
作为补充说明,您可能仍然需要同步处理项目时发生的操作。例如,您需要同步“weightSum”上的增量(或者交替使用AtomicInteger)。您可以使用
ArrayBlockingQueue
。链接:您必须使用ArrayList吗?并发库中的队列如何?这不是一个可复制的示例…@YassinHajaj这就是为什么很难找到并发bug的原因:它们通常是不可复制的。在这种情况下,两个线程之间存在明显的数据竞争。您的示例有两个问题:1)它没有等待另一个线程完成填充goodList
:它等待另一个线程开始填充它。2) 它不同步地访问goodList.isEmpty()
。当线程A更新某个变量(例如,列表的长度)时,无法保证线程B何时(甚至是否)能够看到更改,除非两个线程都在使用某种同步。1。同意。该示例仅为提问者提供一些进展,并不代表应该如何编写正确的多线程代码。2.在while循环中对goodList
进行同步将导致另一个问题:死锁,这就是我忽略它的原因,并尝试阻止可能引发的InterruptedException
。您不需要(或想要!)在睡眠时对锁进行同步,但是,如果轮询长度的线程在从内存获取值时没有在锁上同步,那么Java语言规范就不要求线程看到长度>0,不管其他线程向列表中添加了多少项。当然,在大多数JVM中,轮询线程将看到长度>0,但关键是,除非该程序对于规范允许的每个JVM行为都是正确的,否则不能调用正确的程序。
public class WeightCounter implements Runnable{
private ArrayList<Good> goodList = Main.goodList;
@Override
public void run() {
System.out.println("Thread 2 started");
for(int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
throw AssertionError(ie);
}
int weightSum = 0;
synchronized(goodList){
for (Good g : goodList)
weightSum += g.getWeight();
}
System.out.println(weightSum);
}
}
}
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);
}
}
//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
}
}