CyclicBarrier
Cyclic Barrier is used to block all threads by a checkpoint. When all threads are executed at the checkpoint, the next step will be executed uniformly. Suppose a scenario: Each thread represents a runner. When the runners are ready, they start together. As long as one person is not ready, everyone waits.
Code example:
public class UseCyclicBarrier { static class Runner implements Runnable { private CyclicBarrier barrier; private String name; public Runner(CyclicBarrier barrier, String name) { this.barrier = barrier; this.name = name; } @Override public void run() { try { Thread.sleep(1000 * (new Random()).nextInt(5)); System.out.println(name + " Get ready OK."); barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(name + " Go!!"); } } public static void main(String[] args) throws IOException, InterruptedException { CyclicBarrier barrier = new CyclicBarrier(3); // 3 ExecutorService executor = Executors.newFixedThreadPool(3); executor.submit(new Thread(new Runner(barrier, "zhangsan"))); executor.submit(new Thread(new Runner(barrier, "lisi"))); executor.submit(new Thread(new Runner(barrier, "wangwu"))); executor.shutdown(); } }
Result: Only when OK is ready will the code behind await continue to be executed
wangwu Get ready OK. lisi Get ready OK. zhangsan Get ready OK. zhangsan Go!! lisi Go!! wangwu Go!!
CountDownLacth
CountDownLatch is a counter latch. Its main function is to block the current thread by await() method, then wait for the counter to be reduced to zero, and then wake up these threads to continue executing. Usually used to monitor some initialization operations, waiting for the completion of initialization, notify the main thread to continue working.
Code example:
public class UseCountDownLatch { public static void main(String[] args) { final CountDownLatch countDown = new CountDownLatch(2); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { System.out.println("Thread entry t1" + "Waiting for other threads to finish processing..."); countDown.await(); System.out.println("t1 Threads continue to execute..."); } catch (InterruptedException e) { e.printStackTrace(); } } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { System.out.println("t2 Thread initialization..."); Thread.sleep(3000); System.out.println("t2 Thread initialization is complete, notification t1 Thread continues..."); countDown.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { try { System.out.println("t3 Thread initialization..."); Thread.sleep(4000); System.out.println("t3 Thread initialization is complete, notification t1 Thread continues..."); countDown.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); t3.start(); } }
Result:
t2 threads initialize operations uuuuuuuuuuuu The t3 thread initializes the operation. Enter thread t1 and wait for other threads to finish processing. The t2 thread is initialized and the t1 thread is notified to continue. The t3 thread is initialized and the t1 thread is notified to continue. The t1 thread continues to execute...
The difference between Cyclic Barrier and Count Down Latch
CountDownLact's counter can only be used once, while Cyclic Barrier's counter can be reset using the reset method. So Cyclic Barrier can handle more complex business scenarios. For example, if an error occurs in the calculation, the counter can be reset and the thread can be re-executed.
Cyclic Barrier also provides other useful methods, such as getNumberWaiting, to get the number of threads blocked by Cyclic Barrier. The isBroken() method is used to know whether a blocked thread has been interrupted.
Semaphore
Semaphore is similar to Count Down Latch in that the value of Semaphore can be released after it is obtained, and it is not always reduced to the end like Count Down Latch. It is also used more to limit flow, similar to the function of valves. If some resources are limited to N threads at most, then more than N main threads are not allowed to access them. At the same time, when the existing threads are finished, they are released and new threads are allowed to come in. It is somewhat similar to lock and unlock processes of locks. Relatively speaking, he has two main methods:
- The underlying implementation of acquire() for obtaining permissions is similar to CountDownLatch.countdown().
- The underlying implementation of release() for releasing permissions is an inverse process with acquire().
Current Limitation Strategy at Code Level
Semaphore sema = new Semaphore(5); //Here 5 means accepting up to five threads.
sema.aquire(); // obtain authorization
Code block;
sema.release(); / / release
Code example:
public class UseSemaphore { public static void main(String[] args) { // Thread pool ExecutorService exec = Executors.newCachedThreadPool(); // Only five threads can be accessed simultaneously final Semaphore semp = new Semaphore(5); // Simulate 20 client access for (int index = 0; index < 20; index++) { final int NO = index; Runnable run = new Runnable() { public void run() { try { // Get permission semp.acquire(); System.out.println("Accessing: " + NO); //Simulate actual business logic Thread.sleep((long) (Math.random() * 10000)); // After the visit, release semp.release(); } catch (InterruptedException e) { } } }; exec.execute(run); } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //System.out.println(semp.getQueueLength()); // Exit thread pool exec.shutdown(); } }
Future
Its principle has been introduced before. Now let's see how the Future under concurrent package works.
Code example:
public class UseFuture implements Callable<String>{ private String para; public UseFuture(String para){ this.para = para; } /** * Here is the real business logic, which may be slow to execute. */ @Override public String call() throws Exception { //Analog execution time-consuming Thread.sleep(5000); String result = this.para + "Processing completed"; return result; } //Main control function public static void main(String[] args) throws Exception { String queryStr = "query"; //structure FutureTask,And import classes that require real business logic processing,This class must be implemented. Callable Class of interfaces FutureTask<String> future = new FutureTask<String>(new UseFuture(queryStr)); FutureTask<String> future2 = new FutureTask<String>(new UseFuture(queryStr)); //Create a fixed thread pool with 1 thread count, ExecutorService executor = Executors.newFixedThreadPool(2); //Tasks submitted here future,Open thread execution RealData Of call()Method execution //submit and execute The difference: the first point is submit Importable implementation Callable The second point is the instance object of the interface submit Method has a return value Future f1 = executor.submit(future); //Start a single thread to execute Future f2 = executor.submit(future2); System.out.println("End of request"); try { //Additional data operations can be done here, i.e. the main program executes other business logic. System.out.println("Processing actual business logic..."); Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } //Call the method of obtaining data,If call()Method not executed,Still waiting System.out.println("Data:" + future.get()); System.out.println("Data:" + future2.get()); executor.shutdown(); } }