上記では、主にスレッドプール関連の知識について説明しました。この記事では、主にスレッドプールで使用される補助クラスであるCountDownLatch、CyclicBarrier、およびSemaphoreについて説明します。
1. CountDownLatch
CountDownLatchはjava.util.cucurrentパッケージに存在し、文字通りカウントダウンロックを意味します。
-
countDownLatchクラスを使用すると、スレッドは他のスレッドの実行を待ってから実行できます。
-
この機能は、内部カウンターの属性によって実現されます。スレッドが実行されるたびに、countDown()メソッドが呼び出され、カウンターの値は-1になります。カウンターの値が0の場合、すべてのスレッドが実行され、ロックを待機しているスレッドは次のことができます。作業を再開します。
1.1CountDownLatchクラスで提供される構築メソッド:
//参数count为计数值
public CountDownLatch(int count) {
};
1.2 CountDownLatchクラスの重要なメソッド:
//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public void await() throws InterruptedException {
};
//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
};
//将count值减1
public void countDown() {
};
1.3 CountDownLatchのサンプルコード:
ページサイズを単位とするリスト内の1対1のオブジェクトは、複数のスレッドを開いてデータベース割り当て処理を確認し、対応する結果を取得します。
public class Threads {
public static void main(String[] args) throws Exception{
//定义的一次查库的数量
int pagesize = 3;
//获取需要赋值的user集合
List<User> list = getUserList();
//计算需要并发的线程数
int count = (list.size() + pagesize - 1) / pagesize;
System.out.println("处理"+list.size()+"条数据,需要count:"+count+"个线程执行");
List<User> newList = new ArrayList<>();
//固定线程池
ExecutorService threadPool = Executors.newFixedThreadPool(count);
CountDownLatch countDownLatch = new CountDownLatch(count);
for (int i = 0; i < list.size(); i = i+pagesize) {
TaskService ts = new TaskService(countDownLatch,list.subList(i,i+pagesize<=list.size()?i+pagesize:list.size()));
//获取处理结果
newList.addAll(threadPool.submit(ts).get());
}
try {
//等待所有线程执行完毕
countDownLatch.await();
} catch (InterruptedException e) {
System.out.println("InterruptedException exception"+e);
Thread.currentThread().interrupt();
} finally {
threadPool.shutdown();
}
//输赋值后的结果
for (User user: newList) {
System.out.println(user.getName()+"_"+user.getAge());
}
}
public static List<User> getUserList(){
List<User> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
String name = "user"+i;
list.add(new User(name));
}
return list;
}
}
class TaskService implements Callable<List<User>> {
private CountDownLatch countDownLatch;
private List<User> userList;
TaskService(CountDownLatch countDownLatch,List<User> userList){
this.countDownLatch = countDownLatch;
this.userList = userList;
}
@Override
public List<User> call() throws Exception {
int age = 1;
try{
//模拟查库,循环赋值
for (User u: userList) {
u.setAge(age++);
}
}finally {
//调用方法计数器减1
countDownLatch.countDown();
}
return userList;
}
}
演算結果:
二.CyclicBarrier
文字通りの意味から、このカテゴリーの中国語の意味は「円形の柵」であることがわかります。おおよその意味は、リサイクル可能な障壁です。
その役割は、次のステップに進む前に、すべてのスレッドを完了まで待機させることです。
2.1CyclicBarrierが提供する工法
public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)
パーティーは関係するスレッドの数です
2番目の構築方法にはRunnableパラメーターがあります。これは、タスクが最後にスレッドに到達することを意味します。
1.2 CyclicBarrierクラスの重要なメソッド:
public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
分析:
- await()は、現在のスレッドを一時停止するために使用されます。スレッドがawait()を呼び出すと、バリア状態に到達したことを意味します。
- 2番目のバージョンは、これらのスレッドを一定時間待機させることです。バリア状態に達していないスレッドがある場合、バリアに到達したスレッドは後続のタスクを直接実行します。
2.3 CyclicBarrierクラスのサンプルコード:
public class CyclicBarrierDemo {
public static void main(String[] args) {
int count = 3;
//固定线程池
ExecutorService threadPool = Executors.newFixedThreadPool(count);
CyclicBarrier cb = new CyclicBarrier(count, ()-> {
System.out.println(Thread.currentThread().getName()+"所有线程到达栏栅状态!");
});
for (int i = 0; i < count; i++) {
threadPool.submit(new CyclicBarrierTask(cb));
Thread.sleep(1000L);
}
threadPool.shutdown();
}
}
class CyclicBarrierTask implements Runnable{
private CyclicBarrier cb;
public CyclicBarrierTask(CyclicBarrier cb){
this.cb = cb;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始执行...");
try {
Thread.sleep(1000L);
System.out.println(Thread.currentThread().getName()+"到达栅栏状态!");
cb.await();
System.out.println(Thread.currentThread().getName()+"执行结束。。。");
} catch (Exception e) {
e.printStackTrace();
}
}
}
結果:
上記のサンプルコードの28行をに変換すると
cb.await(1000L,TimeUnit.MILLISECONDS);
指定された時間待機し、スレッドがすべてバリアに達していないことを確認した後、例外がスローされ、次のタスクが続行されることがわかります。
2.4CyclicBarrierの使用シナリオ
複数のスレッドでデータを計算し、最終的に計算結果をマージするために使用できます。または、複数のデータテーブルを同時に保存するなど、操作を続行する前に、各スレッドのタスクが完了するのを待つ必要があります。
2.5CyclicBarrierとCountDownLatchの違い
- CountDownLatchは1回限りで、CyclicBarrierはリサイクル可能です
- CountDownLatchに関係するスレッドの責任は異なり、カウントダウンしているスレッドもあれば、カウントダウンが終了するのを待っているスレッドもあります。CyclicBarrierは、同じスレッドの責任に参加しています。
三セマフォ
セマフォは文字通りセマフォに変換されます。セマフォは同時にアクセスできるスレッドの数を制御できます。acquire()を使用して許可を取得し、許可がない場合は待機し、release()が許可を解放します。
3.1セマフォが提供する工法
public Semaphore(int permits) {
//参数permits表示许可数目,即同时可以允许多少线程进行访问
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
//这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}
2.3セマフォクラスの重要なメソッド:
public void acquire() throws InterruptedException {
} //获取一个许可
public void acquire(int permits) throws InterruptedException {
} //获取permits个许可
public void release() {
} //释放一个许可
public void release(int permits) {
} //释放permits个许可
許可を取得するためにacquire()が使用されます。許可を取得できない場合は、許可が取得されるまで待機します。
release()は、ライセンスを解放するために使用されます。ライセンスをリリースする前に、まずライセンスを取得する必要があることに注意してください。
これらの4つのメソッドはすべてブロックされます。実行結果をすぐに取得する場合は、次のメソッドを使用できます。
public boolean tryAcquire() {
}; //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {
}; //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits) {
}; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException {
}; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
さらに、availablePermits()メソッドを使用して、使用可能な許可の数を取得できます。
工場に5台の機械があり、8人の作業員がいる場合、1台の機械は同時に1人の作業員しか使用できず、他の作業員は使い切った場合にのみ使用し続けることができます。次に、セマフォを介してそれを達成できます。
2.4セマフォクラスのサンプルコード:
ublic class Test {
public static void main(String[] args) {
int N = 8; //工人数
Semaphore semaphore = new Semaphore(5); //机器数目
for(int i=0;i<N;i++)
new Worker(i,semaphore).start();
}
static class Worker extends Thread{
private int num;
private Semaphore semaphore;
public Worker(int num,Semaphore semaphore){
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("工人"+this.num+"占用一个机器在生产...");
Thread.sleep(2000);
System.out.println("工人"+this.num+"释放出机器");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
総括する:
1)CountDownLatchとCyclicBarrierはどちらもスレッド間の待機を実現できますが、焦点は異なります。
CountDownLatchは通常、スレッドAが他のいくつかのスレッドがタスクを実行するのを待ってから実行するために使用されます。
また、スレッドのグループに一般的に使用されるCyclicBarrierは、互いに特定の状態になるまで待機してから、そのスレッドのグループを同時に実行します。
さらに、CountDownLatchは再利用できませんが、CyclicBarrierは再利用できます。
2)セマフォは実際にはロックに似ており、通常、特定のリソースセットへのアクセスを制御するために使用されます。