我現在在學習java.util.concurrent
。我想了解CopyOnWriteArrayList
。在Java中使用CopyOnWriteArrayList
據我所知,這個類看起來像ArrayList
,但線程安全。如果你有很多閱讀和較少的寫作,這個類是非常有用的。
這是我的例子。我如何使用它(僅用於學習目的)?
我可以這樣使用它嗎?
package Concurrency;
import java.util.concurrent.*;
class Entry {
private static int count;
private final int index = count++;
public String toString() {
return String.format(
"index:%-3d thread:%-3d",
index,
Thread.currentThread().getId());
}
}
class Reader implements Runnable {
private CopyOnWriteArrayList<Entry> list;
Reader(CopyOnWriteArrayList<Entry> list) { this.list = list; }
public void run() {
try {
while(true) {
if(!list.isEmpty())
System.out.println("-out " + list.remove(0));
TimeUnit.MILLISECONDS.sleep(100);
}
} catch (InterruptedException e) {
return;
}
}
}
class Writer implements Runnable {
private CopyOnWriteArrayList<Entry> list;
Writer(CopyOnWriteArrayList<Entry> list) { this.list = list; }
public void run() {
try {
while(true) {
Entry tmp = new Entry();
System.out.println("+in " + tmp);
list.add(tmp);
TimeUnit.MILLISECONDS.sleep(10);
}
} catch (InterruptedException e) {
return;
}
}
}
public class FourtyOne {
static final int nThreads = 7;
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<Entry> list = new CopyOnWriteArrayList<>();
ExecutorService exec = Executors.newFixedThreadPool(nThreads);
exec.submit(new Writer(list));
for(int i = 0; i < nThreads; i++)
exec.submit(new Reader(list));
TimeUnit.SECONDS.sleep(1);
exec.shutdownNow();
}
}
很多閱讀和_almost不write_。這很簡單,真的。無論何時你的代碼調用一個修改列表的方法,該方法會創建一個數組的副本,它會修改其副本,然後使用單個原子操作交換舊副本。如果任何其他線程試圖同時訪問列表,另一個線程將訪問舊版本或新版本,但保證不會訪問處於無效狀態的版本。 – 2015-02-23 19:51:23
您正在使用ExecutorService(即線程池)執行永不結束的任務('while(true)...')。不要這樣做。我並不是說它不起作用,但這不是線程池的用途。我會使用'new Thread(...)'或'threadFactory.newThread(...)'來創建一個永不結束的線程。 – 2015-02-23 19:56:22
你的消費者(讀者)一味地相信,如果list.isEmpty()返回true,那麼這個列表就會有一些東西在裏面。這是一個天真的假設。當只有一個消費者時,這是事實,但在許多系統中,消費者不止一個。您應該養成編寫消費者代碼的習慣,如果醒來的事件已經被其他消費者線程服務過,則不會拋出NullPointerException異常。 – 2015-02-23 20:00:41