`

Java多线程(八)之Semaphore、CountDownLatch、CyclicBarrier、Exchanger

 
阅读更多

一、引言

 
Semaphore               :一个计数信号量
CountDownLatch          :一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 
CyclicBarrier           :一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 
   Exchanger               :方便了两个共同操作线程之间的双向交换
 

二、Semaphore

 

Semaphore 是一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。

说白了,Semaphore是一个计数器,在计数器不为0的时候对线程就放行,一旦达到0,那么所有请求资源的新线程都会被阻塞,包括增加请求到许可的线程,也就是说Semaphore不是可重入的。每一次请求一个许可都会导致计数器减少1,同样每次释放一个许可都会导致计数器增加1,一旦达到了0,新的许可请求线程将被挂起。

缓存池整好使用此思想来实现的,比如链接池、对象池等。

 

计数信号可以用于限制有权对资源进行并发访问的线程数。该方法对于实现资源池或限制 Web 爬虫(Web crawler)中的输出 socket 连接非常有用。

 

清单1 对象池

 

[java] view plaincopy
 
  1. package xylz.study.concurrency.lock;  
  2.   
  3. import java.util.concurrent.Semaphore;  
  4. import java.util.concurrent.locks.Lock;  
  5. import java.util.concurrent.locks.ReentrantLock;  
  6.   
  7. public class ObjectCache<T> {  
  8.   
  9.     public interface ObjectFactory<T> {  
  10.   
  11.         T makeObject();  
  12.     }  
  13.   
  14.     class Node {  
  15.   
  16.         T obj;  
  17.   
  18.         Node next;  
  19.     }  
  20.   
  21.     final int capacity;  
  22.   
  23.     final ObjectFactory<T> factory;  
  24.   
  25.     final Lock lock = new ReentrantLock();  
  26.   
  27.     final Semaphore semaphore;  
  28.   
  29.     private Node head;  
  30.   
  31.     private Node tail;  
  32.   
  33.     public ObjectCache(int capacity, ObjectFactory<T> factory) {  
  34.         this.capacity = capacity;  
  35.         this.factory = factory;  
  36.         this.semaphore = new Semaphore(this.capacity);  
  37.         this.head = null;  
  38.         this.tail = null;  
  39.     }  
  40.   
  41.     public T getObject() throws InterruptedException {  
  42.         semaphore.acquire();  
  43.         return getNextObject();  
  44.     }  
  45.   
  46.     private T getNextObject() {  
  47.         lock.lock();  
  48.         try {  
  49.             if (head == null) {  
  50.                 return factory.makeObject();  
  51.             } else {  
  52.                 Node ret = head;  
  53.                 head = head.next;  
  54.                 if (head == null) tail = null;  
  55.                 ret.next = null;//help GC  
  56.                 return ret.obj;  
  57.             }  
  58.         } finally {  
  59.             lock.unlock();  
  60.         }  
  61.     }  
  62.   
  63.     private void returnObjectToPool(T t) {  
  64.         lock.lock();  
  65.         try {  
  66.             Node node = new Node();  
  67.             node.obj = t;  
  68.             if (tail == null) {  
  69.                 head = tail = node;  
  70.             } else {  
  71.                 tail.next = node;  
  72.                 tail = node;  
  73.             }  
  74.   
  75.         } finally {  
  76.             lock.unlock();  
  77.         }  
  78.     }  
  79.   
  80.     public void returnObject(T t) {  
  81.         returnObjectToPool(t);  
  82.         semaphore.release();  
  83.     }  
  84.   
  85. }  


 

清单1描述了一个基于信号量Semaphore的对象池实现。此对象池最多支持capacity个对象,这在构造函数中传入。对象池有一个基于FIFO的队列,每次从对象池的头结点开始取对象,如果头结点为空就直接构造一个新的对象返回。否则将头结点对象取出,并且头结点往后移动。特别要说明的如果对象的个数用完了,那么新的线程将被阻塞,直到有对象被返回回来。返还对象时将对象加入FIFO的尾节点并且释放一个空闲的信号量,表示对象池中增加一个可用对象。

实际上对象池、线程池的原理大致上就是这样的,只不过真正的对象池、线程池要处理比较复杂的逻辑,所以实现起来还需要做很多的工作,例如超时机制,自动回收机制,对象的有效期等等问题。

这里特别说明的是信号量只是在信号不够的时候挂起线程,但是并不能保证信号量足够的时候获取对象和返还对象是线程安全的,所以在清单1中仍然需要锁Lock来保证并发的正确性。

将信号量初始化为 1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态:一个可用的许可,或零个可用的许可。按此方式使用时,二进制信号量具有某种属性(与很多 Lock 实现不同),即可以由线程释放“锁”,而不是由所有者(因为信号量没有所有权的概念)。在某些专门的上下文(如死锁恢复)中这会很有用。

上面这段话的意思是说当某个线程A持有信号量数为1的信号量时,其它线程只能等待此线程释放资源才能继续,这时候持有信号量的线程A就相当于持有了“锁”,其它线程的继续就需要这把锁,于是线程A的释放才能决定其它线程的运行,相当于扮演了“锁”的角色。

 

2.1 信号量的公平性

 

另外同公平锁非公平锁一样,信号量也有公平性。如果一个信号量是公平的表示线程在获取信号量时按FIFO的顺序得到许可,也就是按照请求的顺序得到释放。这里特别说明的是:所谓请求的顺序是指在请求信号量而进入FIFO队列的顺序,有可能某个线程先请求信号而后进去请求队列,那么次线程获取信号量的顺序就会晚于其后请求但是先进入请求队列的线程。这个在公平锁和非公平锁中谈过很多。

 

除了acquire以外,Semaphore还有几种类似的acquire方法,这些方法可以更好的处理中断和超时或者异步等特性,可以参考JDK API。

按照同样的学习原则,下面对主要的实现进行分析。Semaphore的acquire方法实际上访问的是AQS的acquireSharedInterruptibly(arg)方法。这个可以参考http://blog.csdn.net/a511596982/article/details/8275624  一节。

所以Semaphore的await实现也是比较简单的。与CountDownLatch不同的是,Semaphore区分公平信号和非公平信号。

 

清单2 公平信号获取方法

 

[java] view plaincopy
 
  1. protected int tryAcquireShared(int acquires) {  
  2.     Thread current = Thread.currentThread();  
  3.     for (;;) {  
  4.         Thread first = getFirstQueuedThread();  
  5.         if (first != null && first != current)  
  6.             return -1;  
  7.         int available = getState();  
  8.         int remaining = available - acquires;  
  9.         if (remaining < 0 ||  
  10.             compareAndSetState(available, remaining))  
  11.             return remaining;  
  12.     }  
  13. }  


 

清单3 非公平信号获取方法

 

[java] view plaincopy
 
  1. protected int tryAcquireShared(int acquires) {  
  2.     return nonfairTryAcquireShared(acquires);  
  3. }  
  4.   
  5. final int nonfairTryAcquireShared(int acquires) {  
  6.     for (;;) {  
  7.         int available = getState();  
  8.         int remaining = available - acquires;  
  9.         if (remaining < 0 ||  
  10.             compareAndSetState(available, remaining))  
  11.             return remaining;  
  12.     }  
  13. }  


 

对比清单2和清单3可以看到,公平信号和非公平信号在于第一次尝试能否获取信号时,公平信号量总是将当前线程进入AQS的CLH队列进行排队(因为第一次尝试时队列的头结点线程很有可能不是当前线程,当然不排除同一个线程第二次进入信号量),从而根据AQS的CLH队列的顺序FIFO依次获取信号量;而对于非公平信号量,第一次立即尝试能否拿到信号量,一旦信号量的剩余数available大于请求数(acquires通常为1),那么线程就立即得到了释放,而不需要进行AQS队列进行排队。只有remaining<0的时候(也就是信号量不够的时候)才会进入AQS队列。

所以非公平信号量的吞吐量总是要比公平信号量的吞吐量要大,但是需要强调的是非公平信号量和非公平锁一样存在“饥渴死”的现象,也就是说活跃线程可能总是拿到信号量,而非活跃线程可能难以拿到信号量。而对于公平信号量由于总是靠请求的线程的顺序来获取信号量,所以不存在此问题。

 

三、CountDownLatch



3.1 闭锁(Latch)

 

闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态。通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都将通过,但是一旦大门打开,所有线程都通过了,那么这个闭锁的状态就失效了,门的状态也就不能变了,只能是打开状态。也就是说闭锁的状态是一次性的,它确保在闭锁打开之前所有特定的活动都需要在闭锁打开之后才能完成。

 

CountDownLatch是JDK 5+里面闭锁的一个实现,允许一个或者多个线程等待某个事件的发生。CountDownLatch有一个正数计数器,countDown方法对计数器做减操作,await方法等待计数器达到0。所有await的线程都会阻塞直到计数器为0或者等待线程中断或者超时。

 

CountDownLatch的API如下。

  • public void await() throws InterruptedException
  • public boolean await(long timeout, TimeUnit unit) throws InterruptedException
  • public void countDown()
  • public long getCount()

其中getCount()描述的是当前计数,通常用于调试目的。

 

清单4闭锁的两种常见的用法

 

[java] view plaincopy
 
  1. import java.util.concurrent.CountDownLatch;  
  2.   
  3. public class PerformanceTestTool {  
  4.   
  5.     public long timecost(final int times, final Runnable task) throws InterruptedException {  
  6.         if (times <= 0throw new IllegalArgumentException();  
  7.         final CountDownLatch startLatch = new CountDownLatch(1);  
  8.         final CountDownLatch overLatch = new CountDownLatch(times);  
  9.         for (int i = 0; i < times; i++) {  
  10.             new Thread(new Runnable() {  
  11.                 public void run() {  
  12.                     try {  
  13.                         startLatch.await();  
  14.                         //  
  15.                         task.run();  
  16.                     } catch (InterruptedException ex) {  
  17.                         Thread.currentThread().interrupt();  
  18.                     } finally {  
  19.                         overLatch.countDown();  
  20.                     }  
  21.                 }  
  22.             }).start();  
  23.         }  
  24.         //  
  25.         long start = System.nanoTime();  
  26.         startLatch.countDown();  
  27.         overLatch.await();  
  28.         return System.nanoTime() - start;  
  29.     }  
  30.   
  31. }  

 

 

在上面的例子中使用了两个闭锁,第一个闭锁确保在所有线程开始执行任务前,所有准备工作都已经完成,一旦准备工作完成了就调用startLatch.countDown()打开闭锁,所有线程开始执行。第二个闭锁在于确保所有任务执行完成后主线程才能继续进行,这样保证了主线程等待所有任务线程执行完成后才能得到需要的结果。在第二个闭锁当中,初始化了一个N次的计数器,每个任务执行完成后都会将计数器减一,所有任务完成后计数器就变为了0,这样主线程闭锁overLatch拿到此信号后就可以继续往下执行了。

 

根据前面的happend-before法则可以知道闭锁有以下特性:

内存一致性效果:线程中调用 countDown() 之前的操作 happen-before 紧跟在从另一个线程中对应 await() 成功返回的操作。

 

在上面的例子中第二个闭锁相当于把一个任务拆分成N份,每一份独立完成任务,主线程等待所有任务完成后才能继续执行。这个特性在后面的线程池框架中会用到,其实FutureTask就可以看成一个闭锁。后面的章节还会具体分析FutureTask的。

 

同样基于探索精神,仍然需要“窥探”下CountDownLatch里面到底是如何实现await*countDown的。

首先,研究下await()方法。内部直接调用了AQSacquireSharedInterruptibly(1)

 

[java] view plaincopy
 
  1. public final void acquireSharedInterruptibly(int arg) throws InterruptedException {  
  2.     if (Thread.interrupted())  
  3.         throw new InterruptedException();  
  4.     if (tryAcquireShared(arg) < 0)  
  5.         doAcquireSharedInterruptibly(arg);  
  6. }  


 

前面一直提到的都是独占锁(排它锁、互斥锁),现在就用到了另外一种锁,共享锁。

所谓共享锁是说所有共享锁的线程共享同一个资源,一旦任意一个线程拿到共享资源,那么所有线程就都拥有的同一份资源。也就是通常情况下共享锁只是一个标志,所有线程都等待这个标识是否满足,一旦满足所有线程都被激活(相当于所有线程都拿到锁一样)。这里的闭锁CountDownLatch就是基于共享锁的实现。



四、CyclicBarrier 


一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。
在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier


CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),
该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。

清单5:下面是一个在并行分解设计中使用 barrier 的例子,很经典的旅行团例子:

 

[java] view plaincopy
 
  1. import java.text.SimpleDateFormat;    
  2. import java.util.Date;    
  3. import java.util.concurrent.BrokenBarrierException;    
  4. import java.util.concurrent.CyclicBarrier;    
  5. import java.util.concurrent.ExecutorService;    
  6. import java.util.concurrent.Executors;    
  7. public class TestCyclicBarrier {    
  8.   // 徒步需要的时间: Shenzhen, Guangzhou, Shaoguan, Changsha, Wuhan    
  9.   private static int[] timeWalk = { 58151510 };    
  10.   // 自驾游    
  11.   private static int[] timeSelf = { 13445 };    
  12.   // 旅游大巴    
  13.   private static int[] timeBus = { 24667 };    
  14.       
  15.   static String now() {    
  16.      SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");    
  17.      return sdf.format(new Date()) + ": ";    
  18.   }    
  19.   static class Tour implements Runnable {    
  20.      private int[] times;    
  21.      private CyclicBarrier barrier;    
  22.      private String tourName;    
  23.      public Tour(CyclicBarrier barrier, String tourName, int[] times) {    
  24.        this.times = times;    
  25.        this.tourName = tourName;    
  26.        this.barrier = barrier;    
  27.      }    
  28.      public void run() {    
  29.        try {    
  30.          Thread.sleep(times[0] * 1000);    
  31.          System.out.println(now() + tourName + " Reached Shenzhen");    
  32.          barrier.await();    
  33.          Thread.sleep(times[1] * 1000);    
  34.          System.out.println(now() + tourName + " Reached Guangzhou");    
  35.          barrier.await();    
  36.          Thread.sleep(times[2] * 1000);    
  37.          System.out.println(now() + tourName + " Reached Shaoguan");    
  38.          barrier.await();    
  39.          Thread.sleep(times[3] * 1000);    
  40.          System.out.println(now() + tourName + " Reached Changsha");    
  41.          barrier.await();    
  42.          Thread.sleep(times[4] * 1000);    
  43.          System.out.println(now() + tourName + " Reached Wuhan");    
  44.          barrier.await();    
  45.        } catch (InterruptedException e) {    
  46.        } catch (BrokenBarrierException e) {    
  47.        }    
  48.      }    
  49.   }    
  50.   public static void main(String[] args) {    
  51.      // 三个旅行团    
  52.      CyclicBarrier barrier = new CyclicBarrier(3);    
  53.      ExecutorService exec = Executors.newFixedThreadPool(3);    
  54.      exec.submit(new Tour(barrier, "WalkTour", timeWalk));    
  55.      exec.submit(new Tour(barrier, "SelfTour", timeSelf));    
  56. //当我们把下面的这段代码注释后,会发现,程序阻塞了,无法继续运行下去。    
  57.      exec.submit(new Tour(barrier, "BusTour", timeBus));    
  58.      exec.shutdown();    
  59.   }    
  60. }     


 

 

CyclicBarrier最重要的属性就是参与者个数,另外最要方法是await()。当所有线程都调用了await()后,就表示这些线程都可以继续执行,否则就会等待。

 

4.1 CyclicBarrier的特点

 

CyclicBarrier有以下几个特点:

await()方法将挂起线程,直到同组的其它线程执行完毕才能继续
await()方法返回线程执行完毕的索引,注意,索引时从任务数-1开始的,也就是第一个执行完成的任务索引为parties-1,最后一个为0,这个parties为总任务数
CyclicBarrier 是可循环的,显然名称说明了这点。

另外CyclicBarrier除了以上特点外,还有以下几个特点:

如果屏障操作不依赖于挂起的线程,那么任何线程都可以执行屏障操作。

CyclicBarrier 的构造函数允许携带一个任务,这个任务将在0%屏障点执行,它将在await()==0后执行。
CyclicBarrier 如果在await时因为中断、失败、超时等原因提前离开了屏障点,那么任务组中的其他任务将立即被中断,以InterruptedException异常离开线程。
所有await()之前的操作都将在屏障点之前运行,也就是CyclicBarrier 的内存一致性效果(happe)
 

CyclicBarrier 的所有API如下:

public CyclicBarrier(int parties) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。
public CyclicBarrier(int parties, Runnable barrierAction) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。
public int await() throws InterruptedException, BrokenBarrierException 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
public int await(long timeout,TimeUnit unit) throws InterruptedException, BrokenBarrierException,TimeoutException 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。
public int getNumberWaiting() 返回当前在屏障处等待的参与者数目。此方法主要用于调试和断言。
public int getParties() 返回要求启动此 barrier 的参与者数目。
public boolean isBroken() 查询此屏障是否处于损坏状态。
public void reset() 将屏障重置为其初始状态。

 

五、Exchanger 

 

 

Exchanger 类方便了两个共同操作线程之间的双向交换;这样,就像具有计数为 2 的 CyclicBarrier,并且两个线程在都到达屏障时可以“交换”一些状态。(Exchanger 模式有时也称为聚集。)

Exchanger 通常用于一个线程填充缓冲(通过读取 socket),而另一个线程清空缓冲(通过处理从 socket 收到的命令)的情况。当两个线程在屏障处集合时,它们交换缓冲。下列代码说明了这项技术:

清单6:

[java] view plaincopy
 
  1. class FillAndEmpty {  
  2.    Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();  
  3.    DataBuffer initialEmptyBuffer = new DataBuffer();  
  4.    DataBuffer initialFullBuffer = new DataBuffer();  
  5.   
  6.    class FillingLoop implements Runnable {  
  7.      public void run() {  
  8.        DataBuffer currentBuffer = initialEmptyBuffer;  
  9.        try {  
  10.          while (currentBuffer != null) {  
  11.            addToBuffer(currentBuffer);  
  12.            if (currentBuffer.full())  
  13.              currentBuffer = exchanger.exchange(currentBuffer);  
  14.          }  
  15.        } catch (InterruptedException ex) { ... handle ... }  
  16.      }  
  17.    }  
  18.   
  19.    class EmptyingLoop implements Runnable {  
  20.      public void run() {  
  21.        DataBuffer currentBuffer = initialFullBuffer;  
  22.        try {  
  23.          while (currentBuffer != null) {  
  24.            takeFromBuffer(currentBuffer);  
  25.            if (currentBuffer.empty())  
  26.              currentBuffer = exchanger.exchange(currentBuffer);  
  27.          }  
  28.        } catch (InterruptedException ex) { ... handle ...}  
  29.      }  
  30.    }  
  31.   
  32.    void start() {  
  33.      new Thread(new FillingLoop()).start();  
  34.      new Thread(new EmptyingLoop()).start();  
  35.    }  
  36.  }  
 
JDK 6以后为了支持多线程多对象同时Exchanger了就进行了改造(为了支持更好的并发),采用ConcurrentHashMap的思想,将Stack分割成很多的片段(或者说插槽Slot),线程Id(Thread.getId())hash相同的落在同一个Slot上,这样在默认32个Slot上就有很好的吞吐量。当然会根据机器CPU内核的数量有一定的优化,有兴趣的可以去了解下Exchanger的源码。
 
Exchanger实现的是一种数据分片的思想,这在大数据情况下将数据分成一定的片段并且多线程执行的情况下有一定的使用价值。
分享到:
评论

相关推荐

    张孝祥Java多线程与并发库高级应用笔记

    - **高级特性**:包括`ExecutorService`、`Future`、`Semaphore`、`CountDownLatch`、`CyclicBarrier`、`Exchanger`等工具,提供了更灵活、强大的线程调度和控制机制。 - **优势**:减少线程管理的复杂度,提高线程...

    java线程并发semaphore类示例

    Java 5.0引入了四个新的同步工具类,它们是Semaphore、CountDownLatch、CyclicBarrier和Exchanger,用于解决线程间的同步问题。本篇将重点讲解Semaphore类。 Semaphore,顾名思义,就像是一个信号灯或通行证,用来...

    深入了解java多线程

    Java提供了多种机制实现线程间的通信,如`wait()`和`notify()`方法、`CountDownLatch`、`CyclicBarrier`、`Exchanger`等。 ### 4. 多线程应用案例 #### 4.1 异步处理 在用户界面(UI)编程中,多线程可以用于处理...

    java多线程设计模式(pdf)

    4. **并发工具类**:Java并发包(java.util.concurrent)提供了丰富的工具类,如Semaphore(信号量)、CountDownLatch(倒计时器)、CyclicBarrier(回环屏障)和Exchanger(交换器)等。这些工具可以用来协调线程间的行为,...

    深入解析Java多线程

    最后,我们还需要了解Java并发工具类,如Semaphore(信号量)、CountDownLatch(计数器)、CyclicBarrier(回环栅栏)和Exchanger(交换器),它们为多线程编程提供了强大的支持。 总结来说,Java多线程涉及的内容...

    JAVA线程高级-线程按序交替执行

    在Java编程中,多线程是并发编程的重要组成部分,它允许程序同时执行多个任务,从而提高了系统的效率和响应性。然而,在某些场景下,我们可能需要控制线程的执行顺序,确保它们按照特定的顺序交替运行,这在并发编程...

    Java多线程编程之使用Exchanger数据交换实例

    Java多线程编程中的Exchanger是一个非常有用的工具类,它位于`...在设计多线程程序时,根据具体业务场景选择适合的同步工具,如Synchronized、Semaphore、CyclicBarrier、CountDownLatch等,以达到最佳性能和可维护性。

    【2018最新最详细】并发多线程教程

    【2018最新最详细】并发多线程教程,课程结构如下 1.并发编程的优缺点 2.线程的状态转换以及基本操作 3.java内存模型以及happens-before规则 4.彻底理解synchronized 5.彻底理解volatile 6.你以为你真的了解final吗...

    Android多线程全新讲解.pdf

    2. Java多线程高级特性:内容中出现的“ThreadLocal”,“CyclicBarrier”,“CountDownLatch”,“Exchanger”,“ArrayBlockingQueue”,“Lock”,“Condition”和“Semaphore”,这些都是Java 5以后版本引入的...

    面向Java多线程机制的软件重构方法.pdf

    2. 使用并发工具类:如`CountDownLatch`, `CyclicBarrier`, `Semaphore`, `Exchanger`等,提升多线程编程的灵活性和安全性。 3. 线程安全的数据结构:`ConcurrentHashMap`, `CopyOnWriteArrayList`等并发容器可保证...

    Java多线程、并发编程知识点总结.pdf

    8. **并发工具类**:Java 5引入了java.util.concurrent包,提供了丰富的并发工具类,比如CountDownLatch、CyclicBarrier、Semaphore和Exchanger等,它们提供更为高级的线程协作机制。 9. **Future和Callable**:...

    多线程笔记_java/多线程_

    Java提供了一些高级同步工具,如Semaphore(信号量)、CyclicBarrier(循环屏障)、CountDownLatch(倒计时锁)和Exchanger(交换器),它们提供了更灵活的线程同步方式。 四、线程池 Java的ExecutorService和...

    java多线程

    - **并发工具类**:如Semaphore(信号量)、CyclicBarrier(回环栅栏)、CountDownLatch(计数器门闩)和Exchanger(交换器)等。 - **volatile关键字**:保证了多线程环境下变量的可见性,但不保证原子性。 - **...

    JAVA集合、多线程

    4. **并发工具类**:java.util.concurrent包提供了多种并发工具,如Semaphore(信号量)、CountDownLatch(计数器)、CyclicBarrier(回环栅栏)和Exchanger(交换器),它们为多线程编程提供了更高级别的抽象和控制...

    thread.zip--狂神多线程+juc

    《狂神多线程+juc》是一份针对Java并发编程深度学习的资源包,主要涵盖了Java多线程基础以及Java并发工具集(JUC)的高级应用。在Java编程中,多线程是提高程序效率、实现并发执行的重要手段,而JUC(Java ...

    java线程与并发编程实践

    Java提供了一系列并发工具类,如Semaphore(信号量)、CyclicBarrier(循环屏障)、CountDownLatch(计数器门锁)和Exchanger(交换器),它们用于控制线程间访问资源的顺序和数量,协调多个线程间的操作。...

    Java多线程同步器代码详解

    Java多线程同步器在并发编程中扮演着至关重要的角色,它们帮助开发者管理线程间的协作,确保资源的正确访问和程序的顺序执行。本文将深入解析四种主要的同步器:CountDownLatch、Semaphore、Barrier和Exchanger,并...

    JAVA中多线程技术的实现.pdf

    Java 5之后引入的java.util.concurrent包中提供了一系列的并发工具类,如`CountDownLatch`、`CyclicBarrier`、`Semaphore`、`Exchanger`等,使得多线程编程变得更加高效和安全。 以上知识点只是Java多线程技术中的...

    Java开发实战从入门到精通视频教程下载第9章 多线程.zip

    Java并发包(java.util.concurrent)提供了丰富的工具类,如Semaphore(信号量)、CountDownLatch(计数器)、CyclicBarrier(循环屏障)和Exchanger(交换器),帮助开发者更高效地管理多线程。 通过本章的学习,...

    马士兵多线程训练营笔记

    7. **并发工具类**:CountDownLatch、CyclicBarrier、Semaphore、Exchanger等并发工具的用法及应用场景。 8. **并发编程模式**:单例模式的线程安全实现,双检锁(DCL)、静态内部类等方式。 9. **J.U.C(java....

Global site tag (gtag.js) - Google Analytics