3つの同時コンテナとツール
1.並行コンテナ
【1】ConcurrentHashMap
マルチスレッドの場合のHashMap。KEYもVALUEもnullにできないことに注意してください
【2】ConcurrentSkipListMap
マルチスレッドのTreeMap
ConcurrentSkipListMap<String, Integer> concurrentSkipListMap = new ConcurrentSkipListMap<String, Integer>();
concurrentSkipListMap.put("a", 1);
concurrentSkipListMap.put("A", 2);
concurrentSkipListMap.put("0", 3);
concurrentSkipListMap.put("b", 4);
System.out.println(concurrentSkipListMap); // {0=3, A=2, a=1, b=4}
System.out.println("0-" + (int)'0'); // 0-48
System.out.println("A-" + (int)'A'); // A-65
System.out.println("a-" + (int)'a'); // a-97
System.out.println("b-" + (int)'b'); // b-98
【3】ConcurrentSkipListSet
マルチスレッドのTreeSet
【4】CopyOnWriteArrayList
マルチスレッドの場合のArrayList。書き込み操作用のロックと読み取り操作用のロックなし、パフォーマンスの確保
揮発性の変更されたオブジェクト配列。配列が変更されるたびに、新しい配列が作成され、参照がその配列を指します。
add
操作例:
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1); // 创建新的数组
newElements[len] = e;
setArray(newElements); // 指向新的数组
return true;
} finally {
lock.unlock();
}
}
【5】CopyOnWriteArraySet
CopyOnWriteArrayListは内部的に維持されます
private final CopyOnWriteArrayList<E> al;
新しい要素を追加する前に、まず要素がすでに存在するかどうかを判断し、見つからない場合はコンテナに保存されます
/**
* java.util.concurrent.CopyOnWriteArrayList#addIfAbsent(E)方法
*/
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);
}
2.並行性ツール
【1】CountDownLatch
カウントダウンラッチ。事前に統計値を与え、毎回1を引き、0になったら放します
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread(() -> {
System.out.println("A");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown(); // 减1
}).start();
new Thread(() -> {
System.out.println("B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown(); // 减1
}).start();
try {
countDownLatch.await(); // Main线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束!");
【2】CyclicBarrier
循環障壁。事前に統計値を与え、毎回1を足して、統計値が足したら手放します
方法1を使用します。
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3); // 记得算上Main线程
new Thread(() -> {
System.out.println("A");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
cyclicBarrier.await(); // 等待(加1)
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
System.out.println("B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
cyclicBarrier.await(); // 等待(加1)
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
try {
cyclicBarrier.await(2, TimeUnit.SECONDS); // 只等待2秒钟(加1)
} catch (InterruptedException | BrokenBarrierException | TimeoutException e) {
e.printStackTrace();
}
System.out.println("结束!");
}
使用法2:
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> {
System.out.println("结束!");
}); // 其他线程都执行完毕后,执行该线程方法
new Thread(() -> {
System.out.println("A");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
cyclicBarrier.await(); // 等待(加1)
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
System.out.println("B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
cyclicBarrier.await(); // 等待(加1)
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
【3】セマフォ
信号。一度に実行できるスレッドの数を指定します
int loop = 10;
int permits = 2;
ExecutorService executorService = Executors.newFixedThreadPool(loop);
Semaphore semaphore = new Semaphore(permits);
for (int i = 0; i < loop; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " ---> 执行任务");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
});
}
if (!executorService.isShutdown()) {
executorService.shutdown();
}
【4】エクスチェンジャー
交換器。スレッド間のデータ交換に使用されます。スレッドが交換を開始すると、常に別のスレッドが交換を実行するのを待ちます。長く待つのが怖い場合は、このpublic V exchange(V x, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException
方法を使用できます
Exchanger<String> exchanger = new Exchanger<String>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "发出了消息,等待回复");
String exchange = exchanger.exchange("hello?");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "收到了:" + exchange);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程A").start();
new Thread(() -> {
try {
Thread.sleep(3000);
String exchange = exchanger.exchange("hi!");
System.out.println(Thread.currentThread().getName() + "收到了:" + exchange + ",并且已回复");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程B").start();