Summary of JUC Concurrency Tools

Introduction

JUC encapsulates many concurrent tool classes to conveniently control the synchronous access between concurrent threads and reduce the repeated development of code.

1. CountDownLatch

  • By setting an initial value to CountDownLatch, and then blocking the program from running, the program can continue to execute when the initial value of the counter becomes 0

Pseudo code case

  • Divide the big task of calculating annual income into 3 small tasks to execute concurrently.
  • The 3 small tasks are calculating income from January to April, calculating income from May to September, and calculating income from October to December
  • Wait until the 3 small tasks are executed, then go to the annual income based on the results of the 3 calculation tasks
CountDownLatch latch = new CountDownLatch(3);  //计数器值为3

// 给每个任务创建一个线程并发执行
//线程1执行操作A {
    
    
	do something .. 计算1-4月收入
    latch.countDown();   //计数器值减 1
}

//线程2执行操作B {
    
    
	do something ...计算5-9月收入
    latch.countDown();   //计数器值减 1
}

//线程3执行操作C {
    
    
	do something ...计算10-12月收入
    latch.countDown();   //计数器值减 1
}

//阻塞当前线程,直到latch的值为0才继续向下执行
 latch.await();

//对年收入进行汇总
计算年收入 = 1-4月收入 + 5-9月收入 + 10-12月收入 ....

2. CyclicBarrier

  • Loop barrier, which is a bit like a loopable loop counter (calculates the number of threads waiting)

  • For the loop barrier, CyclicBarrier sets a number of waiting threads A (barrier point), and then each thread waits for each other, until the number of waiting threads reaches A (barrier point), all the waiting threads will continue to execute together. As shown below

  • Insert picture description here

  • CyclicBarrier internally maintains whether the CyclicBarrier is broken ("broken") through a boolean variable. When the barrier is broken, all functions fail, and all threads waiting on the barrier lock are emptied and awakened.

Barrier damage:

  • When CyclicBarrier is initialized, broken=false means that the barrier is not broken.
  • If the waiting thread is interrupted, broken=true , indicating that the barrier is broken.
  • If the waiting thread times out, broken=true , indicating that the barrier is broken.
  • If a thread calls the CyclicBarrier.reset() method, then broken=false , indicating that the barrier returns to an unbroken state.

Pseudo code case

  • There are 100 people participating in the activity, and everyone is waiting to form a team, and every 3 people can form a team. You can start participating in the activity. Then continue to cycle.
 //创建CyclicBarrier 屏障 ,每当等待线程的数量达到3后就会执行这个线程回调方法
 CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
    
    
      public void run() {
    
    
          System.out.println("3名人员组队成功, 开始参与活动");
      }
  });

//创建10个线程并发去抢占活动名额
 for (int i = 0; i < 100; i++) {
    
    
     new Thread(()->{
    
    
     	Thread.sleep(3000);
        
		System.out.println(Thread.currentThread().getName() + "-到达,等待组队完成");
		// 当Thread-3到来时,由于是vip之前等待的线程不算要重新开始
		if(Thread.currentThread().getName().equals("Thread-3")) {
    
    
			 //将屏障设置为初始状态,清空在屏障锁等待的线程.这样barrier就可以重复使用
            barrier.reset(); 
        }
		
		  try {
    
    
		  	// 线程等待, CyclicBarrier还需达到屏障点的等待线程数-1
            //(当屏障状态破损,此方法不生效)当前线程进入此屏障锁的等待队列
            //当等待超过1分钟都没到达屏障点时置屏障为破损状态
            barrier.await(1, TimeUnit.SECONDS);
        } catch (Exception e) {
    
    
        }

		// 参加完活动后,程序继续往下执行
		3人组队参与活动结束各回各家各找爸妈....

     }).start();
 }


 // 每隔0.5秒统计等待组成人员个数  	----监控等待线程数
 new Thread(()->{
    
    
      while(true) {
    
    
         Thread.sleep(500);
         
        //获取在循环屏障的等待线程个数
          System.out.println("等待的线程数 " + barrier.getNumberWaiting()); 
        //屏障是否破损,为true就是破损,需reset重置才能循环使用
          System.out.println("is broken " + barrier.isBroken());  
      }
  }).start();


The difference between CyclicBarrier and CountDownLatch

  • CountDownLatch is disposable, CyclicBarrier provide reset function is recycled to
  • CountDownLatch is that a thread B waits until the other N threads are completed, and then thread B continues to execute. And CyclicBarrier is that N threads all reach the same horizontal line and start running together

2.1 Await method source code

  • That is, before the real blockage, it is not necessary to block if it is judged whether to reach the barrier point, and then judge whether it is broken, judge whether the barrier needs to be broken, and then go to the real blockage. Therefore, it is said that when the barrier is broken, it cannot be used.
 private int dowait(boolean timed, long nanos){
    
    
   final ReentrantLock lock = this.lock;
	lock.lock();
	
	  final Generation g = generation;
	  
	 // 屏障破损直接抛出异常 
 	if (g.broken)
       throw new BrokenBarrierException();

	// 线程被中断
   if (Thread.interrupted()) {
    
    
       breakBarrier(); // 打破屏障
       throw new InterruptedException();
   }

	int index = --count;// 屏障点-1
	// 触发屏障点
	if (index == 0) {
    
     
         boolean ranAction = false;
          try {
    
    
              final Runnable command = barrierCommand;
              if (command != null)
                  command.run(); //执行设置的回调任务
              ranAction = true;
              nextGeneration();  // 循环初始化下一次屏障功能
              return 0;
          } finally {
    
    
              if (!ranAction)
                  breakBarrier();
          }
     }

	// 未达到屏障点
	 for (;;) {
    
    
           try {
    
    
           		// 非超时等待直接在条件锁Condition上等待即可
                if (!timed)
                    trip.await(); 
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            }
            // 如果线程被中断 
			catch (InterruptedException ie) {
    
    
				// 当屏障未破损时
                if (g == generation && ! g.broken) {
    
    
                    breakBarrier(); //打破屏障
                    throw ie;
                } else {
    
    
                	// 重新设置中断标志位
                    Thread.currentThread().interrupt();
                }
            }

            if (g.broken)
                throw new BrokenBarrierException();

            if (g != generation)
                return index;

            if (timed && nanos <= 0L) {
    
    
                breakBarrier();
                throw new TimeoutException();
            }
        }
	   

	lock.unlock();
}
//打破屏障:  恢复为非破损状态,并唤醒在此CyclicBarrier锁上等待队列的线程
 private void breakBarrier() {
    
    
    generation.broken = true;
     count = parties;
     trip.signalAll();
 }

 // 重置CyclicBarrier,开启下一轮循环
 private void nextGeneration() {
    
    
        // 唤醒在此CyclicBarrier锁上等待队列的线程n
        trip.signalAll();
        count = parties;
        generation = new Generation();
  }

3. Semaphore

  • signal
  • Control the number of threads accessing synchronized resources
//
Semaphore s = new Semaphore(10);

public void toExecute(){
    
    
   semaphore.acquire();
	... do something
	semaphore.release();
}

//最多有10个线程能同时执行 toExecute()方法
for(;;){
    
    
	new Thread(()->{
    
    
			 toExecute();
	}).start

}


4. Exchanger

  • Data exchange tool between threads for inter-thread communication and data exchange
  • A package tool class used to exchange data between two worker threads. Simply put, one thread wants to exchange data with another thread, and the first thread that takes out the data will wait for the second thread until the second Threads can exchange corresponding data with each other when they arrive with data

Insert picture description here

  • Combining the above figure and the pseudo code below, add when thread A first executes to exchange.exchange("data"); then thread A will fill the Object1 space with data "data", and then thread A will block. Soon after thread B executes to exc .exchange("bbq") So thread B fills the data "bbq" into the Object2 space and then thread B also enters the block. Exchanger finds that the data in Object1 space and Object2 space are not empty at this time, so Exchanger will send Object1 space and Object2 space The data in the Object1 space is exchanged. At this time, the data in the Object1 space is "bbq", and the data in the Object2 space is "data". After the exchange is completed, thread A returns from the blocked state, and returns the data "bbq" of Object1 to res1 to receive. Thread B also returns from the blocked state and returns the data "data" of Object2 to res2 to receive

Fake code:

//交换String类型数据
Exchanger<String> exch = new Exchanger<>();

//线程A {
    
    
 String res1 =  exch.exchange(“data”);    //在交换前会阻塞在此
}

//线程B里 {
    
    
 String res2 = exch.exchange(“bbq”);	    //在交换前会阻塞在此
}

//两个线程同时执行,当两个线程都存储了数据后,会把数据交换并返回
这时res1结果为bbq, res2结果为data

5 LockSupport

  • In the thread blocking tool class, all methods are static methods, allowing the thread to block at any position. Of course, there must be a way to wake up after blocking.
  • Functionally similar to waiting/notifying mechanism
class TheradA {
    
    
  public void run(){
    
    
    	// do something ....
    	LoackSupport.park(); // 阻塞当前线程
  }
}

//
ThreadA a = new TheradA();
a.start();

LockSupport.unpark(a); // 唤醒线程a

Different from wait/notify mechanism

  • Both wait and notify are methods in Object. The wait and notify methods must be called in the synchronized block synchronized. The lock object must be acquired before calling these two methods. Park can lock without acquiring a lock on an object. Live thread.
  • Notify can only randomly select a thread to wake up, and cannot wake up a specified thread, but unpark can wake up a specified thread.

Guess you like

Origin blog.csdn.net/weixin_41347419/article/details/107132770