所以我有两个线程在运行,其中一个线程应该从用户那里获取信息,另一个线程假设使用用户提供的信息,如下所示:
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等待另一个,因为除了这些数据共享之外,他们还需要完成其他任务。我怎么能这样做呢?
答案 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同步工具,例如
wait/notify
机制有关详细信息,您必须阅读规范。我们来看一个使用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对象输入的新值,则:
例如,您可以这样做:
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在某种包装类中实例化队列,但它看起来并不像你正在采用那条路径。