目录
下面是一个使用Semaphore实现共享锁的例子:
下面是一个使用CountDownLatch实现等待一组操作完成的例子:
下面是一个使用CyclicBarrier实现等待一组线程达到某个状态后再同时执行的例子:
结论1:
结论2:
下面是一个使用Semaphore
实现共享锁的例子:
package cn.net.cdsz.ccb.test;
import java.util.concurrent.Semaphore;
public class test {
private static Semaphore semaphore = new Semaphore(2);
public static void main(String[] args) {
Runnable task = () -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " acquired semaphore");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println(Thread.currentThread().getName() + " released semaphore");
}
};
new Thread(task, "Thread 1").start();
new Thread(task, "Thread 2").start();
new Thread(task, "Thread 3").start();
}
}
在上面的代码中,创建了三个线程,它们都需要获取一个初始值为2的信号量。因为初始值为2,所以前两个线程可以同时获取信号量,而第三个线程需要等待前面两个线程释放信号量之后才能够获取它。运行程序可以看到,前两个线程同时获取到了信号量,并且在获取到信号量之后都阻塞了1秒钟。最后,它们释放了信号量,第三个线程才能够获取它。
下面是一个使用CountDownLatch
实现等待一组操作完成的例子:
package cn.net.cdsz.ccb.test;
import java.util.concurrent.CountDownLatch;
public class test {
public static void main(String[] args) throws InterruptedException {
int n = 3;
CountDownLatch latch = new CountDownLatch(n);
Runnable task = () -> {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " finished its work");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
};
for (int i = 0; i < n; i++) {
new Thread(task, "Thread " + i).start();
}
latch.await();
System.out.println("All threads finished their work");
}
}
在上面的代码中,创建了三个线程,它们都需要执行一些耗时的操作。在主线程中,通过CountDownLatch
等待这三个线程执行完成后再继续执行。
CyclicBarrier
用于等待一组线程达到某个状态后再同时执行。它通过维护一个计数器和一个栅栏状态来实现。计数器的初始值由构造函数设置。每个线程需要达到某个状态时,需要通过await()
方法等待其他线程都达到这个状态,此时计数器的值就会减1。当计数器的值变为0时,所有线程就会被唤醒,同时继续执行。
下面是一个使用CyclicBarrier实现等待一组线程达到某个状态后再同时执行的例子:
package cn.net.cdsz.ccb.test;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class test {
public static void main(String[] args) {
int numThreads = 3;
CyclicBarrier barrier = new CyclicBarrier(numThreads);
for (int i = 0; i < (numThreads+1); i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " is waiting at the barrier.");
try {
barrier.await();
System.out.println(Thread.currentThread().getName() + " has crossed the barrier.");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
我们创建了一个CyclicBarrier对象,并指定了需要等待的线程数量为3。然后我们创建了三个线程,并让它们执行一个任务:打印出自己正在等待栅栏,然后调用await()方法等待其他线程,最后再打印出自己已经通过栅栏。
当所有线程都调用了await()方法后,栅栏就会打开,所有线程就可以同时通过栅栏继续执行后续的代码了。
结论1:
在输出"Thread-3 has crossed the barrier."之后,第4个线程就已经执行完毕退出了,所以不会有"Thread-3 has finished."的输出。这里 CyclicBarrier 体现出跟 Semaphore 的区别,Semaphore 是继续等待,CyclicBarrier 是直接执行不再等待锁
结论2:
CyclicBarrier和CountDownLatch的区别在于,CyclicBarrier要求等待的线程数量是固定的,并且只有所有线程都调用了await()方法之后,才能继续执行后续的代码。而CountDownLatch则是一个计数器,可以任意地增加或减少计数器的值,并且只要计数器的值不为0,等待线程就会一直被阻塞