CountDownLatch
- CountDownLatch allows one or more threads to wait for other threads to complete their operations.
- CountDownLatch can replace join and provide richer usage.
- CountDownLatch's countDown method, N decreases by 1; CountDownLatch's await method blocks the current thread until N becomes zero.
- CountDownLatch is unlikely to reinitialize or modify the value of the CountDownLatch object's internal counter.
- CountDownLatch is internally implemented by AQS shared locks.
public class CountDownLatchTest {
private static final CountDownLatch DOWN_LATCH = new CountDownLatch(2);
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
System.out.println(1);
DOWN_LATCH.countDown();
System.out.println(2);
DOWN_LATCH.countDown();
}).start();
DOWN_LATCH.await();
System.out.println("3");
}
}
CyclicBarrier
- CyclicBarrier sets up a barrier (also known as a synchronization point) to block a set of threads until the last thread reaches the barrier before the barrier opens and all threads blocked by the barrier continue to run.
- The default construction method for CyclicBarrier is CyclicBarrier (int parties), whose parameters represent the number of threads blocked by the barrier. Each thread calls the await method to tell CyclicBarrier that I have reached the barrier, and then the current thread is blocked.
- CyclicBarrier also provides a more advanced constructor, CyclicBarrier (int parties, Runnable barrierAction), which prioritizes barrierAction when a thread reaches a barrier, making it easier to handle more complex business scenarios.
- The getNumberWaiting method can get the number of threads blocked by CyclicBarrier; the isBroken() method is used to know if the blocked thread has been interrupted.
- CyclicBarrier's counters can be reset using the reset() method (CountDownLatch's counters can only be used once).So CyclicBarrier can handle more complex business scenarios.For example, if an error occurs in the calculation, you can reset the counter and have the thread execute again.
- CyclicBarrier can be used to compute data with multiple threads and finally merge the computed scenarios.
- CyclicBarrier is implemented internally with a reentrant lock ReentrantLock.
public class BankWaterService implements Runnable {
// Create four barriers and execute the run method of the current class after processing
private CyclicBarrier barrier = new CyclicBarrier(4, this);
// Assuming there are four calculation tasks, only four threads are started
private Executor executor = Executors.newFixedThreadPool(4);
// Save calculation results for each task
private ConcurrentHashMap<String, Integer> sheetBankWaterCount = new ConcurrentHashMap<>();
private AtomicInteger atomicInteger = new AtomicInteger(1);
private void count() {
for (int i = 0; i < 4; i++) {
Thread thread = new Thread(() -> {
// Calculation results for current tasks are ignored
sheetBankWaterCount.put(Thread.currentThread().getName(), 1);
// Calculate complete, insert a barrier
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, "thread" + atomicInteger.getAndIncrement());
executor.execute(thread);
}
}
@Override
public void run() {
int result = 0;
// Summarize the results calculated for each task
for (Map.Entry<String, Integer> sheet : sheetBankWaterCount.entrySet()) {
result += sheet.getValue();
}
//Output Results
sheetBankWaterCount.put("result", result);
System.out.println(result);
}
public static void main(String[] args) {
BankWaterService bankWaterCount = new BankWaterService();
bankWaterCount.count();
}
}
Semaphore
- Semaphore (semaphore) is used to control the number of threads that simultaneously access a specific resource by coordinating the threads to ensure the rational use of public resources.
- Semaphore can be used for traffic control, especially in scenarios where public resources are limited, such as database connections.
- Semaphore's construction method Semaphore(int permits) accepts an integer number indicating the number of licenses available.
- The thread first acquires a license using Semaphore's acquire() method, then calls the release() method to return the license after use.You can also try to obtain a license using the tryAcquire() method.
- intavailablePermits(): Returns the number of licenses currently available in this semaphore.
- intgetQueueLength(): Returns the number of threads waiting to acquire a license.
- booleanhasQueuedThreads(): Is there a thread waiting to acquire a license.
- Semaphore is implemented internally using AQS shared locks.
public class SemaphoreTest {
private static final int THREAD_COUNT = 30;
private static ExecutorService EXECUTOR = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore SEMAPHORE = new Semaphore(10);
private static AtomicInteger ATOMICINTEGER = new AtomicInteger(1);
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
EXECUTOR.execute(() -> {
try {
SEMAPHORE.acquire();
System.out.println("save data" + ATOMICINTEGER.getAndIncrement());
SEMAPHORE.release();
} catch (InterruptedException e) {
}
});
}
EXECUTOR.shutdown();
}
}
Exchanger
- Exchanger is a tool class for interthread collaboration - data exchange between threads.It provides a synchronization point at which two threads can exchange data with each other.The two threads exchange data through the exchange method, and if the first thread executes the exchange() method first, it waits until the second thread executes the exchange method as well.
- An Exchanger object can be simply understood as a container containing two grids that can be filled with information by the exchanger method.When both lattices are filled, the object automatically exchanges information between the two lattices and returns it to the thread to exchange information between the two threads.
- Exchanger can be used in genetic algorithms.(Genetic Algorithms: Two people need to be selected as the mating object, in which case their data will be exchanged and the crossover rule will be used to get the mating result)
- Exchanger can be used for proofreading tasks, such as a data that requires two people to proofread at the same time, and that can be corrected before subsequent processing.In this case, you can use Exchanger to compare the results of two proofreading copies.
- Exchanger is implemented internally with a lock-free CAS. Exchange uses two properties of the internal object Node, item and match, to distribute and store the values of the two threads.
public class ExchangerTest {
private static final Exchanger<String> exchange = new Exchanger<>();
private static ExecutorService threadPool = Executors.newFixedThreadPool(2);
public static void main(String[] args) {
threadPool.execute(() -> {
try {
String result = exchange.exchange("data A");
System.out.println("A Of exchange Result:" + result);
} catch (InterruptedException e) {
}
});
threadPool.execute(() -> {
try {
String result = exchange.exchange("data B");
System.out.println("B Of exchange Result:" + result);
} catch (InterruptedException e) {
}
});
threadPool.shutdown();
}
}