在两个线程之间共享一个ArrayList?

时间:2016-10-30 03:14:51

标签: java multithreading arraylist concurrency

所以我有两个线程在运行,其中一个线程应该从用户那里获取信息,另一个线程假设使用用户提供的信息,如下所示:

public class UserRequest implements Runnable {

@Override
public void run() {
    // TODO Auto-generated method stub
    String request;
    Scanner input = new Scanner(System.in);
    while(true)
    {
        System.out.println("Please enter request:");
        request = input.nextLine();
        try
        {
            //do something
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }

}

第二个帖子:

public class Poller implements Runnable {

ArrayList<String> colors = new ArrayList<String>();

public void poll()
{
    for(String color : colors)
    {
        if(color == "")
        {
            //do work
        }
        else
        {
            //do work
        }
    }
}

@Override
public void run() {

    colors.add("Violet");
    colors.add("Green");
    colors.add("Yellow");
    colors.add("Orange");

    while(true)
        poll();     
}
}

我想要做的是接受用户在UserRequest对象中输入的任何输入,并推入ArrayList Poller对象中的BlockingQueue,以便它可以“工作”新值同样。我看过像select * from contact where FullName='Alliance Française' 这样的东西,但我不想让Thread等待另一个,因为除了这些数据共享之外,他们还需要完成其他任务。我怎么能这样做呢?

5 个答案:

答案 0 :(得分:5)

由于您使用了动词'push'和'poll',因此您似乎在寻找Queue而不是List

因此,我认为您正在寻找ConcurrentLinkedQueue,记录here

它允许您让UserRequest个对象提供它,并让Poller个对象使用它。

虽然由于开放式Poller没有任何while,您的wait对象似乎会有相当高的CPU消耗:

public class Poller implements Runnable {
  Queue<String> colors = new ConcurrentLinkedQueue<String>();

  public void poll() {
    while(this.colors.isEmpty()){
      Thread.currentThread().wait();
    }

    String color = this.colors.poll();

    while(color != null) {
      if(color == "") {
        //do work

      } else {
        //do work
      }

      color = this.colors.poll();
    }
  }

  @Override
  public void run() {
    colors.offer("Violet");
    colors.offer("Green");
    colors.offer("Yellow");
    colors.offer("Orange");

    while(true) {

      this.poll();
    }
  }
}

此代码需要进行一些更改才能运行,但它几乎包含您需要的所有内容。 它的作用非常简单:它一直保持轮询,直到没有剩余元素为止。 一旦发生这种情况,Poller对象会询问它当前Thread是否处于休眠状态,因为没有Queue中没有元素的情况下运行它。

public class UserRequest implements Runnable {

  @Override
  public void run() {
    String request;
    Scanner input = new Scanner(System.in);

    while(true) {
      System.out.println("Please enter request:");
      request = input.nextLine();

      try {
        //do something

      } catch(IOException e) {
        e.printStackTrace();

      } finally {
        this.notifyAll(); // Notifies all sleeping threads to wake up
      }
    }
  }

如果您发现,我只在notifyAll课程中添加了UserRequest来电。为什么?非常简单:notifyAll唤醒所有wait Thread s,这正是所有Poller没有元素的情况。

一旦被调用,Poller将被唤醒,检查他们的颜色Queue是否有元素并使用它们。如果Queue没有元素,他们会再次睡眠,直到UserRequest再次唤醒它们,依此类推。

答案 1 :(得分:3)

有两种方法可以解决这个问题:

1)它使用thread safe collection,如ConccurentLinkedQueue用于生产者 - 消费者,工作消费等的逻辑。如果你想使用实现List interface的类(因此,您可以采用与通常ArrayList相同的方法,您必须查看CopyOnWriteArrayList的一面,但请注意,此类使用阻止同步。

2)另一种方法是使用内置的java同步工具,例如

有关详细信息,您必须阅读规范。我们来看一个使用Semaphore的例子:

private final Semaphore semaphore = new Semaphore(2, true);

   public void appendToList() throws InterruptedException {
     available.acquire();
     arrayList.add(.....); //put here what u need
   }

   public void putItem(Object x) {
     if (someLogicHere(x)) //semaphore releases counter in this place
       available.release();
   }

当然,您可以结合使用所有这些内容,例如:您可以同时使用一些semaphores,或使用差异工具。

答案 2 :(得分:1)

“但我不希望任何一个Thread等待另一个,因为除了这种数据共享之外,他们还需要完成其他任务。”

没有办法实现这一目标。任何正确的类线程总是会遇到一个问题,即你需要让一个线程等待,而另一个线程需要做什么。但问题是你要尽量减少这一点。您希望仅导致线程非常短暂地停止并且很少,并且仅在不这样做的情况下将导致它发生故障。您可以使用其中一个同步数据结构,也可以自己编写一些同步代码。

唯一有问题的对象是arraylist,你希望在任一个线程上都有绝对最小的停顿量。因此,您可能希望根据arraylist本身的对象进行同步。因此,只需在访问arraylist对象的点周围写几个小同步块。

public class Poller implements Runnable {

    ArrayList<String> colors;

    public Poller(ArrayList<String> colors) {
        this.colors = colors;
        //pass in colors object, if modified from the scanner side it must synchronize the block around the colors object too.
    }

    public void doWork(String color) {
        //do work
    }

    public void addColor(String color) {
        synchronized (colors) {
            colors.add(color);
        }
    }

    @Override
    public void run() {
        while (!Thread.interrupted())
            if (!colors.isEmpty()) {
                String color;
                synchronized (colors) {
                    color = colors.remove(0);
                }
                doWork(color); //work done outside synch
            }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

重点是永远不要同时删除或添加内容。你不能整个循环遍历列表,因为如果工作是在循环中完成的,那么这是一个问题,并且数组的大小可能会改变,所以你不知道它是多少。但是,您可以使用ArrayList,只需同步更改数据结构的代码块,并从该同步块中获取字符串,然后对其进行处理。这样, only 停顿是一个线程正在读取或写入的简短瞬间,另一个需要。两者都是非常快的操作。

答案 3 :(得分:0)

如果要访问用户从poller对象输入的新值,则:

  • 由于对象存储在堆中,而不是在Poller类中创建arrayList的新实例,您可以从UserRequest发送列表对象的引用。当yu更改时添加新值它将在userRequest中的arrayList中反映在Poller使用的arrayList中。

例如,您可以这样做:

 public class UserRequest implements Runnable {

private ArrayList<String> arrayList  = new ArrayList<String>();

@Override
public void run() {
    // TODO Auto-generated method stub
    String request;
    Scanner input = new Scanner(System.in);
    while(true)
    {
        System.out.println("Please enter request:");
        request = input.nextLine();
        try
        {

         Poller poller = new Poller(arrayList);
         Thread t = new Thread(poller);
         t.start();

        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }

}

您可以像这样更改您的Poller类:

 public class Poller implements Runnable {
  private ArrayList arrayList = null;    

  Poller(ArrayList<String> arrayList){
     this.arrayList = arrayList; 
   }

public void poll()
{
    for(String color : arrayList)
    {
        if(color == "")
        {
            //do work
        }
        else
        {
            //do work
        }
    }
}

@Override
public void run() {

       while(true){
        poll();   
     }    
}

但是不是在无限循环中调用池,而是应该向arrayList添加一个监听器,这样只有在将新值添加到List时才调用poll()

您可以查看此链接,了解有关向ArrayList添加监听器的详情:https://stackoverflow.com/a/16529462/7083385

答案 4 :(得分:0)

您可以使用队列。队列有自己的poll方法。你可以让它静止,但我怀疑这是最好的方法。一般来说,我使用spring在某种包装类中实例化队列,但它看起来并不像你正在采用那条路径。