[英]Sharing an ArrayList between two threads?
因此,我有兩個線程正在運行,其中一個線程應該從用戶那里獲取信息,而另一個線程應該使用用戶提供的信息,如下所示:
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
東西,但是我不希望其中一個線程等待另一個,因為它們除了共享數據之外還需要完成其他任務。 我該怎么做呢?
由於您使用過動詞'push'和'poll',因此您似乎在尋找Queue
而不是List
。
因此,我認為您正在尋找此處記錄的ConcurrentLinkedQueue
。
它可以讓你有你的UserRequest
對象喂養它和你的Poller
對象來使用它。
盡管由於打開while
您的Poller
對象似乎有很高的CPU消耗, while
沒有任何wait
:
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
}
}
}
如果您注意到,我只向您的UserRequest
類添加了notifyAll
調用。 為什么? 非常簡單: notifyAll
喚醒所有正在wait
Thread
,這恰好是所有不帶元素的Poller
所做的事情。
調用后, Poller
將會喚醒,檢查其color Queue
是否包含元素並與之配合使用。 如果Queue
沒有元素,它們將再次休眠,直到UserRequest
再次喚醒它們,依此類推。
有兩種方法可以解決此問題:
1)它使用thread safe collection
,例如ConccurentLinkedQueue
用於生產者-消費者,作業或其他方面的邏輯。如果要使用實現List interface
的類(因此可以使用與通常ArrayList
相同的方法),則必須查看CopyOnWriteArrayList
的一側,但請注意,此類使用阻塞同步。
2)另一種方法是使用內置的Java同步工具,例如
wait/notify
機制 有關更多詳細信息,您必須閱讀規范。 讓我們考慮一個使用信號量的示例:
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
,或使用diff工具。
“但是我不希望任何一個線程都等待另一個線程,因為它們除了共享數據之外還需要完成其他任務。”
沒有辦法做到這一點。 該類的任何正確線程將始終遭受這樣的問題:您將需要等待一個線程,而另一個線程需要執行某些操作。 不過,重點是您希望將其最小化。 您只希望使線程非常短暫且很少停頓,並且僅在這種情況下才會導致停頓。 您可以使用一種同步數據結構,也可以自己編寫一些同步代碼。
唯一有問題的對象是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,只需同步代碼塊,即可在其中更改數據結構並從該同步塊中獲取字符串,然后對其進行處理。 這樣, 唯一的停頓就是一個線程正在讀取或寫入而另一個線程需要讀取的短暫瞬間。 兩者都是非常快速的操作。
如果要訪問用戶從輪詢器對象輸入的新值,則:
例如,您可以這樣操作:
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();
}
}
但是,而不是在無限循環中調用pool,您應該將監聽器添加到arrayList,以便僅在將新值添加到List時才調用poll()
。
您可以查看此鏈接以了解有關將偵聽器添加到ArrayList
更多信息: https : //stackoverflow.com/a/16529462/7083385
您可以使用隊列。 隊列有自己的輪詢方法。 您可以將其設為靜態,但是我懷疑這是最好的方法。 通常,我使用spring來在某種包裝類中實例化隊列,但它看起來並不像您所選擇的那樣。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.