"JUC 잠금"원칙과 예제의 교환기 교환기 (12) - 자바 일련의 멀티 스레딩

기 프로필

교환기는 처음부터 제공 JDK1.5에서 도구입니다, 일반적으로 두 개의 작업자 스레드 사이에 데이터를 교환하는 데 사용됩니다. 

 일정 소개

     private static final int ASHIFT = 7; // 两个有效槽(slot -> Node)之间的字节地址长度(内存地址,以字节为单位),1 << 7至少为缓存行的大小,防止伪共享
     private static final int MMASK = 0xff; // 场地(一排槽,arena -> Node[])的可支持的最大索引,可分配的大小为 MMASK + 1
     private static final int SEQ = MMASK + 1; // bound的递增单元,确立其唯一性
     private static final int NCPU = Runtime.getRuntime().availableProcessors(); // CPU的个数,用于场地大小和自旋控制
     static final int FULL = (NCPU >= (MMASK << 1)) ? MMASK : NCPU >>> 1; // 最大的arena索引
     private static final int SPINS = 1 << 10; // 自旋次数,NCPU = 1时,禁用
     private static final Object NULL_ITEM = new Object();// 空对象,对应null
     private static final Object TIMED_OUT = new Object();// 超时对象,对应timeout

Ashift, 2 개의 홈 (7) 사이의 유효 어드레스 길이는 1 << (최소 캐시 라인의 크기, 잘못된 공유 문제를 피하기 위하여 아래 참고)

Mmask가 멀티 슬롯 교환 최대 인덱스를 지원할 수, 크기 MMASK + 1 (인덱스 0에서 시작)

고유성 결정 SEQ 바운드 증가 부 (높음)

번호 NCPU, CPU의

FULL하지 MMASK보다 더 큰 분야 인덱스; 아레나 슬롯 행 좋은 유연성을 얻기 위해, 슬롯이 모든 스레드 경합을 회피.

> 수율 - -> 블록 스핀의 스핀 - 스핀 대기 주파수에서 대기 스핀이어서 초경량

 

더미는 지역의 원리에 따라, 데이터를 교환, 캐시 메모리와 캐시 라인 단위를 공유하는 사이에, 인접하는 데이터 어드레스 공간은 캐시와 동일한 데이터 블록 (캐시 라인)에로드되고, 상기 어레이는 연속 메모리 주소 공간 때문에, 슬롯의 복수의 동일한 캐시 라인 슬롯 변화에로드한다 (논리가 가상 메모리에 관한) (다른 캐시 라인을 포함하는 슬롯에 대한 모든 데이터를 야기 슬롯) 무효, 메모리에서 다시로드 할 필요가 성능에 영향을 미칩니다.

따라서, 이러한 상황을 방지하기 위해, 데이터는 유효 슬롯이 같은 캐시 라인에로드되지 않습니다 그래서, 작성 될 필요가있다. 도 3에 도시 된 바와 같이 필러의 크기는, 1 << (7)이다.

 

데이터 구조 노드 

    static final class Node {
        int index; // arena的索引
         int bound; // 记录上次的bound
         int collides; // 当前bound下CAS失败的次数
         int hash; // 伪随机,用于自旋
         Object item; // 当前线程携带的数据
         volatile Object match; // 存放释放线程携带的数据
         volatile Thread parked; // 挂在此结点上阻塞着的线程
     }

인덱스, 경기장 색인

바인딩, 바인딩 마지막 레코드

충돌은 충돌 == m까지 유효 인덱스가 슬롯을 통해 이송 있으며, 오른쪽에서 왼쪽으로 이송, 하한, CAS 실패 m의 최대 값 m에 따라 결합의 현재 수는 (결합 및 MMASK) 현재의 최대 유효 인덱스 시간 슬롯이 성장해야 성장 모드는 리셋됩니다 바인딩 (서열 업데이트 버전에 따라 높은, +1, 낮음), 리셋 충돌하면서

스핀에 대한 의사 랜덤 해시,

항목 데이터는 현재 스레드에 의해 수행

, 캐리 데이터 (교환 스레드) 스토리지 출시 스레드를 일치

주차, 스레드 대기를 차단이 접합에 매달려 발매 예정

아래를 참조하십시오

참가자 데이터 구조

     // 每个线程携带一个Node
     static final class Participant extends ThreadLocal<Node> {
         public Node initialValue() {
             return new Node();
         }
     }

직접 현재의 thread 저장의 ThreadLocal로부터 상속 된 참가자는 노드는 노드 교환 작업의 동작에 주로 의존하고 수행하는

소개 속성

     private final Participant participant;// 每个线程携带一个Node
     private volatile Node[] arena; // 场地,Node数组
     private volatile Node slot;// 槽,单个Node
     private volatile int bound;// 当前最大有效arena索引,高8位+SEQ确立其唯一性,低8位记录有效索引

행 인덱스 기록 된 최대 유효 경쟁 (슬롯이 가득) 아레나 동적 변화, 증가, 감소 오픈 타임 슬롯. 바인딩 +의 SEQ +/- 1의 + 상 1 (SEQ은 명령과 동일한 + 1) +1 후 (예를 들어, 고유의 속성의 버전을 결정하고, -1, 실제로 결합 된 두 가지 버전이며, 충돌이 재설정 인덱스의 왼쪽 탐색 오른쪽에서 너무 오른쪽으로 순서대로 찾아 왼쪽으로 빠르게 빈 자리를 찾을 때 바인딩, 그것을 포착하려고, 일반적으로 오른쪽 슬롯 치열한 경쟁 슬롯보다 왼쪽에, 업데이트해야합니다 우리가 올바른 때문에 업데이트 할 가장 독특한 버전을 구속하지 않을 경우, 인덱스 업데이트 없다되었습니다에 가까운 왼쪽에 있기 때문에, 물러 슬롯의 오른쪽에 있어야 인덱스를 가로 지르는 더한 마이너스 하나씩, 슬롯 경쟁이 치열한 트래버스 왼쪽뿐만 아니라, 정의의 유산 감소 할 수밖에되었을 것입니다,하지만 영향은 매우 효율적입니다 그래서이 증가하고있다.), +/- 1 개 낮은 실질 실효 지수 (mMASK)

아래와 같이

교환 방법

     public V exchange(V x) throws InterruptedException {
         Object v;
         Object item = (x == null) ? NULL_ITEM : x; // 转换成空对象
         // arena == null, 路由到slotExchange(单槽交换), 如果arena != null或者单槽交换失败,且线程没有被中断,则路由到arenaExchange(多槽交换),返回null,则抛出中断异常
         if ((arena != null || (v = slotExchange(item, false, 0L)) == null)
                 && ((Thread.interrupted() || (v = arenaExchange(item, false, 0L)) == null)))
             throw new InterruptedException();
         return (v == NULL_ITEM) ? null : (V) v;
     }

첫째, 인터럽트 예외가 계속되지 않았다가 발생합니다, 경기장이 null가 null, 또는 slotExchange 방법입니다 반환하지 않는 경우, slotExchange 메소드를 호출 한 다음, 현재의 thread가 (인터럽트 플래그) 중단 여부를 결정 null의 경우 무대가 null 여부를 결정 arenaExchange 메소드를 호출 메소드 리턴 null의 경우, 예외 인터럽트 복귀 결과를 던진다.

시간 제한와의 교류 방법

     public V exchange(V x, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
         Object v;
         Object item = (x == null) ? NULL_ITEM : x;// 转换成空对象
         long ns = unit.toNanos(timeout);
         // arena == null, 路由到slotExchange(单槽交换), 如果arena != null或者单槽交换失败,且线程没有被中断,则路由到arenaExchange(多槽交换),返回null,则抛出中断异常
         if ((arena != null || (v = slotExchange(item, true, ns)) == null)
                 && ((Thread.interrupted() || (v = arenaExchange(item, true, ns)) == null)))
             throw new InterruptedException();
         if (v == TIMED_OUT)// 超时
             throw new TimeoutException();
         return (v == NULL_ITEM) ? null : (V) v;
     }

저두 요, 플러스 판사 타임 아웃.

slotExchange 방법

     private final Object slotExchange(Object item, boolean timed, long ns) {
         Node p = participant.get(); // 获取当前线程携带的Node
         Thread t = Thread.currentThread(); // 当前线程
         if (t.isInterrupted()) // 保留中断状态,以便调用者可以重新检查,Thread.interrupted() 会清除中断状态标记
             return null;
         for (Node q;;) {
             if ((q = slot) != null) { // slot不为null, 说明已经有线程在这里等待了
                 if (U.compareAndSwapObject(this, SLOT, q, null)) { // 将slot重新设置为null, CAS操作
                     Object v = q.item; // 取出等待线程携带的数据
                     q.match = item; // 将当前线程的携带的数据交给等待线程
                     Thread w = q.parked; // 可能存在的等待线程(可能中断,不等了)
                     if (w != null)
                         U.unpark(w); // 唤醒等待线程
                     return v; // 返回结果,交易成功
                 }
                 // CPU的个数多于1个,并且bound为0时创建 arena,并将bound设置为SEQ大小
                 if (NCPU > 1 && bound == 0 && U.compareAndSwapInt(this, BOUND, 0, SEQ))
                     arena = new Node[(FULL + 2) << ASHIFT]; // 根据CPU的个数估计Node的数量
             } else if (arena != null)
                 return null; // 如果slot为null, 但arena不为null, 则转而路由到arenaExchange方法
             else { // 最后一种情况,说明当前线程先到,则占用此slot
                 p.item = item; // 将携带的数据卸下,等待别的线程来交易
                 if (U.compareAndSwapObject(this, SLOT, null, p)) // 将slot的设为当前线程携带的Node
                     break; // 成功则跳出循环
                 p.item = null; // 失败,将数据清除,继续循环
             }
         }
         // 当前线程等待被释放, spin -> yield -> block/cancel
         int h = p.hash; // 伪随机,用于自旋
         long end = timed ? System.nanoTime() + ns : 0L; // 如果timed为true,等待超时的时间点; 0表示没有设置超时
         int spins = (NCPU > 1) ? SPINS : 1; // 自旋次数
         Object v;
         while ((v = p.match) == null) { // 一直循环,直到有线程来交易
             if (spins > 0) { // 自旋,直至spins不大于0
                 h ^= h << 1; // 伪随机算法, 目的是等h小于0(随机的)
                 h ^= h >>> 3;
                 h ^= h << 10;
                 if (h == 0) // 初始值
                     h = SPINS | (int) t.getId();
                 else if (h < 0 && (--spins & ((SPINS >>> 1) - 1)) == 0)
                     Thread.yield(); // 等到h < 0, 而spins的低9位也为0(防止spins过大,CPU空转过久),让出CPU时间片,每一次等待有两次让出CPU的时机(SPINS >>> 1)
             } else if (slot != p) // 别的线程已经到来,正在准备数据,自旋等待一会儿,马上就好
                 spins = SPINS;
             // 如果线程没被中断,且arena还没被创建,并且没有超时
             else if (!t.isInterrupted() && arena == null && (!timed || (ns = end - System.nanoTime()) > 0L)) {
                 U.putObject(t, BLOCKER, this); // 设置当前线程将阻塞在当前对象上
                 p.parked = t; // 挂在此结点上的阻塞着的线程
                 if (slot == p)
                     U.park(false, ns); // 阻塞, 等着被唤醒或中断
                 p.parked = null; // 醒来后,解除与结点的联系
                 U.putObject(t, BLOCKER, null); // 解除阻塞对象
             } else if (U.compareAndSwapObject(this, SLOT, p, null)) { // 超时或其他(取消),给其他线程腾出slot
                 v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
                 break;
             }
         }
         // 归位
         U.putOrderedObject(p, MATCH, null);
         p.item = null;
         p.hash = h;
         return v;
     }

개요

슬롯이 비어 (NULL)인지 1.는, 데이터를 교환 스레드에 대한 직업의 성공과 대기 및 대기 스레드, 트랜잭션의 끝, 수익을 깨워 경우, 슬롯을 점유하려고 스레드가이 대기에 이미있는 것을 나타내는 비어 있지.

2. 경기장을 만들 수 있지만 슬롯이 비어 때까지 슬롯을 점유하려고, 또는 거래의 성공적인 마감을 반환 압류 [1 단계] 계속 실패 슬롯이 점유합니다.

 

슬롯이 비어있는 경우 3.이 경기장은 경기장이 방법 arenaExchange 다시 라우팅, 빈 반환 null가 아닌 경우, 비어 여부가 결정된다

경기장이 비어있는 경우 (4), 현재의 thread가 도착하는 것을 나타내는, 루프에서, 자신의 직업에 대한 슬롯을 표시합니다 성공하면, 슬롯이 시도 단계 [5]에 계속 실패 할 경우, 단계를 계속 [1]

5 현재 스레드의 대기는 순서를 기다리는 첫 번째 스핀 (회전)이며, 출시하는 스핀 (블록) 차단에 마지막으로 충분한 CPU의 타임 슬라이스 (수율), 최대 실패 주면 -> 수율 -> 블록

제한 시간은 (설정 시간 초과 경우) 또는 중단 될 경우 6. 다음 루프를 종료합니다.

7. 최종적으로, 데이터는 다음 재사용을 재설정 한 결과, 단부를 반환한다.

아래를 참조하십시오

arenaExchange 방법

     private final Object arenaExchange(Object item, boolean timed, long ns) {
         Node[] a = arena; // 交换场地,一排slot
         Node p = participant.get(); // 获取当前线程携带的Node
         for (int i = p.index;;) { // arena的索引,数组下标
             int b, m, c;
             long j; // 原数组偏移量,包括填充值
             // 从场地中选出偏移地址为(i << ASHIFT) + ABASE的内存值,也即真正可用的Node
             Node q = (Node) U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE);
             if (q != null && U.compareAndSwapObject(a, j, q, null)) { // 此槽位不为null, 说明已经有线程在这里等了,重新将其设置为null, CAS操作
                 Object v = q.item; // 取出等待线程携带的数据
                 q.match = item; // 将当前线程携带的数据交给等待线程
                 Thread w = q.parked; // 可能存在的等待线程
                 if (w != null)
                     U.unpark(w); // 唤醒等待线程
                 return v; // 返回结果, 交易成功
             } else if (i <= (m = (b = bound) & MMASK) && q == null) { // 有效交换位置,且槽位为空
                 p.item = item; // 将携带的数据卸下,等待别的线程来交易
                 if (U.compareAndSwapObject(a, j, null, p)) { // 槽位占领成功
                     long end = (timed && m == 0) ? System.nanoTime() + ns : 0L; // 计算出超时结束时间点
                     Thread t = Thread.currentThread(); // 当前线程
                     for (int h = p.hash, spins = SPINS;;) { // 一直循环,直到有别的线程来交易,或超时,或中断
                         Object v = p.match; // 检查是否有别的线程来交换数据
                         if (v != null) { // 有则返回
                             U.putOrderedObject(p, MATCH, null); // match重置,等着下次使用
                             p.item = null; // 清空,下次接着使用
                             p.hash = h;
                             return v; // 返回结果,交易结束
                         } else if (spins > 0) { // 自旋
                             h ^= h << 1;
                             h ^= h >>> 3;
                             h ^= h << 10; // 移位加异或,伪随机
                             if (h == 0) // 初始值
                                 h = SPINS | (int) t.getId();
                             else if (h < 0 && // SPINS >>> 1, 一半的概率
                                     (--spins & ((SPINS >>> 1) - 1)) == 0)
                                 Thread.yield(); // 每一次等待有两次让出CPU的时机
                         } else if (U.getObjectVolatile(a, j) != p)
                             spins = SPINS; // 别的线程已经到来,正在准备数据,自旋等待一会儿,马上就好
                         else if (!t.isInterrupted() && m == 0 && (!timed || (ns = end - System.nanoTime()) > 0L)) {
                             U.putObject(t, BLOCKER, this); // 设置当前线程将阻塞在当前对象上
                             p.parked = t; // 挂在此结点上的阻塞着的线程
                             if (U.getObjectVolatile(a, j) == p)
                                 U.park(false, ns); // 阻塞, 等着被唤醒或中断
                             p.parked = null; // 醒来后,解除与结点的联系
                             U.putObject(t, BLOCKER, null); // 解除阻塞对象
                         } else if (U.getObjectVolatile(a, j) == p && U.compareAndSwapObject(a, j, p, null)) {
                             if (m != 0) // 尝试缩减
                                 U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1); // 更新bound, 高位递增,低位 -1
                             p.item = null; // 重置
                             p.hash = h;
                             i = p.index >>>= 1; // 索引减半,为的是快速找到汇合点(最左侧)
                             if (Thread.interrupted())// 保留中断状态,以便调用者可以重新检查,Thread.interrupted() 会清除中断状态标记
                                 return null;
                             if (timed && m == 0 && ns <= 0L) // 超时
                                 return TIMED_OUT;
                             break; // 重新开始
                         }
                     }
                 } else
                     p.item = null; // 重置
             } else {
                 if (p.bound != b) { // 别的线程更改了bound,重置collides为0, i的情况如下:当i != m, 或者m = 0时,i = m; 否则,i = m-1; 从右往左遍历
                     p.bound = b;
                     p.collides = 0;
                     i = (i != m || m == 0) ? m : m - 1; // index 左移
                 } else if ((c = p.collides) < m || m == FULL || !U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) { // 更新bound, 高位递增,低位 +1
                     p.collides = c + 1;
                     i = (i == 0) ? m : i - 1; // 左移,遍历槽位,m == FULL时,i == 0(最左侧),重置i = m, 重新从右往左循环遍历
                 } else
                     i = m + 1; // 槽位增长
                 p.index = i;
             }
         }
     }

개요

1. 오프셋 어드레스 필드에서 선택되는 (I << ASHIFT) + 염기, 메모리 값, 즉, i 번째의 실제 사용 가능한 노드,이 슬롯은 상기 단계에 비어 비어 있는지 여부를 판정한다 [2]; 이다 성공, 교환 데이터를 포착, 슬롯을 점유하는 노력이 대기에서 스레드가있는 것을 나타내는, 비어있는, 및 종료, 대기 스레드 수익을 깨어 있지하며 [9]로 단계로, 성공을 포착하지

(ⅰ m 대)를 [단계 (9)]으로, 경계 아웃 바운드의 인덱스를 확인 2.;없는 한계 다음 단계.

3. 시도는 실패 잡아, [단계 1] 입력 슬롯을 점유하는, 성공, 다음 단계를 포착.

데이터를 교환, 끝으로 교환 스레드 데이터 (있는 경우)이있는 경우, 경기를 확인하십시오, 그렇지 않으면, 다음 단계를.

5. 스핀이 0 다음 단계보다 크지 않은 경우, 0보다 큰 경우, 0보다 체크 해시 적게 0이 아닌 경우 0 인 스핀 반 스텝 입력이 아닌 경우 [4] 그 경우, 그래서 그 CPU 시간 후, 들어간다 [단계 4]

그렇지 않으면 다음 단계는 상기 인터럽트를 확인 6. m은 단계 [4]로하고, 차단 시간 초과가, 중단없이하지 초과 여부, 최소 값에 도달하고, 최소의 값 m에 도달한다.

다시 시작하는 최초의 슬롯을 삭제하려는 데이터를 교환하는 방법 7. 쓰레드는 [단계 4]를 입력하는 데 실패 폐기하고, 그렇지 않으면, 다음 단계를 포함한다.

제 1 결합 마이너스 (m> 0), 인덱싱 절반; 여부 확인 인터럽트 또는 제한하고, 그렇지 않은 경우, 단계 [1]를 입력하고, 그렇지 않으면 최종.

9.이 리셋 충돌 변경된 경우, 변경된 결합되면, 인덱스 m 리셋 좌측이나 조향 단계 [1], 그렇지 않으면, 다음 단계를 포함한다.

10.의 충돌은 단계 [13] 그렇지 않으면, 다음 단계를 들어, 최대 값에 도달하고, 그렇지 않은 경우.

11 m에 도달 FULL는 단계 [13]에 입력되고, 그렇지 않으면 다음 단계.

그렇지 않으면 다음 단계; CAS (12)가 결합 더한 성공이고, 성공하면, I는 m + 1로 설정되고, 슬롯 성장 단계 [1] 진행한다.

제 1 충돌 플러스, 좌 지수 [단계 1] 입력

(] 오른쪽 위의 그림에 사진 마우스를 참조 [? -> [이미지 열기 (I) 새 탭에서] -> [클릭하면 확대 (+) 벡터])은 아래를 참조하십시오

위험한

     private static final sun.misc.Unsafe U;
     private static final long BOUND;
     private static final long SLOT;
     private static final long MATCH;
     private static final long BLOCKER;
     private static final int ABASE;
     static {
         int s;
         try {
             U = sun.misc.Unsafe.getUnsafe();
             Class<?> ek = Exchanger.class;
             Class<?> nk = Node.class;
             Class<?> ak = Node[].class;
             Class<?> tk = Thread.class;
             BOUND = U.objectFieldOffset(ek.getDeclaredField("bound"));
             SLOT = U.objectFieldOffset(ek.getDeclaredField("slot"));
             MATCH = U.objectFieldOffset(nk.getDeclaredField("match"));
             BLOCKER = U.objectFieldOffset(tk.getDeclaredField("parkBlocker"));
             s = U.arrayIndexScale(ak); // 数组增量地址
             ABASE = U.arrayBaseOffset(ak) + (1 << ASHIFT); // 数组首元素偏移地址
         } catch (Exception e) {
             throw new Error(e);
         }
         if ((s & (s - 1)) != 0 || s > (1 << ASHIFT))
             throw new Error("Unsupported array scale");
     } 

S 배열의 요소가 상기 주소 공간의 각각에 의해 점유 첫번째 배열 요소 염기, 거짓 공유 방지 오프셋

마지막으로, 무대가 새로운 노드 [(FULL + 2) << ASHIFT를] = FULL이 <= MMASK는 스케일 <= 1 << ASHIFT은 (FULL + 2) << ASHIFT 노드 바와 실제로 사용할 FULL +이고 2, 실제로 FULL + 1 번째는, 아무 소용이 마지막,하지만 또한 거짓 공유를 방지하기 위해, 마지막으로 사용하는 경우, 그래서 오른쪽하지가 가득 차 있음을, 다른 데이터 수정, 즉의 발생이 영향을 미칠 수 거짓 공유 문제. 최대 유효 인덱스 MMASK (결합 및 MMASK)이지만, 전체 슬롯을 통해 전혀 성장 의지주기가 데이터를 교환 할 때 m (실제 최대 인덱스) 증가.

의사

H H << ^ = 1; H = H >>> ^ 3; H ^ = H << 10;

실제로 xorshift 알고리즘, T = 좌 L 대표 오른쪽의 대표 R, A, B, C는 각각 상기 화학식 3을 나타낸다 (I + 라) (I + Rb는) (I + LC), 10, I는 {0,1} 인 32 비트 (INT), 이진 INT 총의 매트릭스를 나타내는, 임의의 알고리즘에 의해 표현된다 T. 번역은 상기 화학식이다 : H ^ = H << 1] H = H >>> ^ 3] H = H << ^ 10.

왜 우리는 대 략 그것을 사용해야합니까?

사실, 의사 난수는, 진정으로 무작위가 아니라 임의의 효과를 달성하기 위해 시뮬레이션 알고리즘, 희망은 더 큰 더 나은의 기간입니다. A 입력을 부여하고, 시간이 제 입력의 출력과 동일 할 때까지, 등 다음 입력으로 생성하여 출력하고, 경우주기의 랜덤 알고리즘 랜덤 숫자로 발생하는 소위 사이클 수단. 이 개념으로, 우리는 테스트중인 코드를 작성할 수 있습니다.

시각 (이 0이면 항상 0 랜덤 언급되지 않으며, 출력이 0이 아닌, 단일 행렬 []), INT 형 최대 기간이 타입의 값을 모두 통과한다 추정하고, 최대 - 최소 = 232 --1

자바 코드

 public class PseudoRandom {
     private static final Map<Long, StringBuilder> map = new ConcurrentHashMap<>();
 
     public static void random(int a, int b, int c) {
         long cnt = 0;
         int h = 1;
         do {
             h ^= h << a;
             h ^= h >>> b;
             h ^= h << c;
             cnt++;
         } while (h != 1);
 
         StringBuilder builder = map.get(cnt);
         if (builder == null) {
             builder = new StringBuilder();
             map.put(cnt, builder);
         }
 
         builder.append(" (" + a + ", " + b + ", " + c + ")");
     }
 
     public static void main(String[] args) {
         CountDownLatch latch = new CountDownLatch(11 * 11 * 11);
         ExecutorService s = Executors.newFixedThreadPool(10);
         for (int i = 1; i < 11; i++) { // i, j ,k实际上应该是31,这里仅为了说明问题,当改成31时,CountDownLatch应该初始化为31 * 31 * 31
             for (int j = 1; j < 11; j++) {
                 for (int k = 1; k < 11; k++) {
                     final int ii = i;
                     final int jj = j;
                     final int kk = k;
                     s.execute(new Runnable() {
                         @Override
                         public void run() {
                             random(ii, jj, kk);
                             latch.countDown();
                         }
                     });
                 }
             }
         }
 
         s.shutdown();
         try {
             latch.await(300, TimeUnit.SECONDS);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
         }
 
         TreeMap<Long, StringBuilder> t = new TreeMap<Long, StringBuilder>(Collections.reverseOrder());
         t.putAll(map);
 
         for (Map.Entry<Long, StringBuilder> entry : t.entrySet()) {
             System.out.println("[" + entry.getKey() + "]" + entry.getValue().toString());
         }
     }
 }

출력 당 사이클 역순, 즉 최대 전방 

[4294967295] (1, 3, 10) (2, 7, 7) (2, 7, 9) (5, 9, 7) (7, 1, 9) (7, 7, 2) (7, 9, 5)
 [4160749537] (1, 7, 9) (4, 1, 9) (6, 5, 9)
 [3900702255] (1, 3, 4) (5, 5, 7) (7, 5, 5)
 [3758096377] (1, 9, 2) (2, 9, 1) (7, 7, 9)
 [2147483647] (1, 5, 5) (1, 9, 6) (2, 5, 5) (2, 5, 7) (5, 5, 1) (5, 5, 2) (6, 5, 7) (6, 9, 1) (7, 5, 2) (7, 5, 6)
 [2147483644] (1, 9, 10)
 [2147213313] (2, 5, 3) (3, 5, 2)
 [2147188740] (4, 5, 5) (4, 9, 1) (5, 5, 4)
 [2145385473] (7, 9, 9)
 [2145382404] (1, 5, 9)
 [2143288833] (5, 1, 6) (6, 1, 5)
 [2139094020] (1, 7, 6)
 [2113929153] (1, 5, 4) (4, 5, 1)
 [2080374753] (2, 3, 3) (3, 3, 2)
 [1997533470] (2, 9, 9)
 [1879048185] (2, 5, 9) (4, 7, 9)
 [1747831785] (8, 9, 5)
 [1610612733] (7, 3, 10)
 [1560280902] (3, 5, 5) (5, 5, 3)
 [1431655765] (1, 7, 7) (2, 9, 5) (5, 1, 8) (5, 9, 2) (7, 7, 1) (8, 1, 5)
 [1431562923] (1, 1, 2) (2, 1, 1)
 [1430257323] (3, 9, 7) (7, 9, 3)
 [1409286123] (5, 3, 7) (7, 3, 5) (9, 1, 10)
 [1339553285] (1, 9, 5) (5, 9, 1)
 [1242911789] (3, 7, 10) (5, 3, 10)
 [1174405085] (1, 3, 5) (5, 3, 1) (9, 3, 4)
 [1073741823] (3, 1, 6) (6, 1, 3)
 [1073594370] (1, 9, 4)
 [1064182911] (4, 3, 7) (7, 3, 4)
 [1006632930] (3, 1, 10)
 [714429611] (3, 1, 4) (4, 1, 3)
...

그것은 첫 번째 행에서 볼 수 있습니다 정확히 232--1 (략) 4294967295 기간을 발생

복수의 그룹의 행은 동일한 기간으로 표시.

질문, 왜이 왼쪽과 오른쪽에 하나습니까? 사실, 한 번만 또는 종속 영양 임의의 결과를 얻을 수있었습니다.

처음 왼쪽에 대해 높은 것보다 더 많은 수 있도록하는 것입니다 아마 때문에, 이것에 대한 이유를 추측, 오른쪽, 두 번째 왼쪽을 그 높은 낮은이 포함되도록, 하나 저보다 더 많은 수 임의성을 높이는 것입니다 그것은 진정으로 무작위이다.

예 기 

두 스레드 개의 버퍼, 데이터 버퍼가 작성되는 스레드는 다른 스레드는 다른 내부 버퍼로부터 데이터를 인출한다. 스레드가 채워진 버퍼 데이터를 기입하거나, 빈 버퍼의 데이터 걸릴 데이터 스레드를 취할 때, 다른 하나는 다른 교환 버퍼에 동작을 개시하고, 교환의 타이밍은 버퍼가 가득 있다는 버퍼가 비어 있습니다. 다음 코드는 더 각주가없는, 매우 간단합니다.

public class FillAndEmpty {
     Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
     DataBuffer initialEmptyBuffer = DataBuffer.allocate(1024);
     DataBuffer initialFullBuffer = DataBuffer.allocate(1024);
 
     class FillingLoop implements Runnable {
         public void run() {
             DataBuffer currentBuffer = initialEmptyBuffer;
             try {
                 while (currentBuffer != null) {
                     addToBuffer(currentBuffer);
                     if (currentBuffer.isFull()) {
                         System.out.println("[FillingLoop](Before)" + currentBuffer);
                         currentBuffer = exchanger.exchange(currentBuffer);
                         System.out.println("[FillingLoop](After)" + currentBuffer);
                     }
                 }
             } catch (InterruptedException ex) {
                 Thread.currentThread().interrupt();
             }
         }
     }
 
     class EmptyingLoop implements Runnable {
         public void run() {
             DataBuffer currentBuffer = initialFullBuffer;
             try {
                 while (currentBuffer != null) {
                     takeFromBuffer(currentBuffer);
                     if (currentBuffer.isEmpty()) {
                         System.out.println("[EmptyingLoop](Before)" + currentBuffer);
                         currentBuffer = exchanger.exchange(currentBuffer);
                         System.out.println("[EmptyingLoop](After)" + currentBuffer);
                     }
                 }
             } catch (InterruptedException ex) {
                 Thread.currentThread().interrupt();
             }
         }
     }
 
     void start() {
         Thread fillingLoopThread = new Thread(new FillingLoop());
         Thread emptyingLoopThread = new Thread(new EmptyingLoop());
 
         fillingLoopThread.start();
         emptyingLoopThread.start();
 
         try {
             Thread.sleep(10);
         } catch (InterruptedException e) {
             // do nothing
         }
         fillingLoopThread.interrupt();
         emptyingLoopThread.interrupt();
     }
 
     public void takeFromBuffer(DataBuffer buf) {
         buf.take();
     }
 
     public void addToBuffer(DataBuffer buf) {
         buf.add(1);
     }
 
     private static class DataBuffer {
         private final int[] buf;
         private final int size;
         private int index;
 
         private DataBuffer(int size) {
             this.size = size;
             this.buf = new int[size];
         }
 
         public static DataBuffer allocate(int size) {
             return new DataBuffer(size);
         }
 
         public boolean isEmpty() {
             return index == 0;
         }
 
         public boolean isFull() {
             return index == size - 1;
         }
 
         public int take() {
             if (index > 0) {
                 return buf[index--];
             }
 
             return -1;
         }
 
         public void add(int data) {
             if (index < size - 1) {
                 buf[index++] = data;
             }
         }
     }
 
     public static void main(String[] args) {
         FillAndEmpty fae = new FillAndEmpty();
         fae.start();
     }
 }

출력이 교환 전후 다음, 두 개의 스레드를 보유 버퍼 데이터 역전. 

 [EmptyingLoop](Before)com.luoluo.exchanger.FillAndEmpty$DataBuffer@1733c6a5
 [FillingLoop](Before)com.luoluo.exchanger.FillAndEmpty$DataBuffer@39bcfec1
 [FillingLoop](After)com.luoluo.exchanger.FillAndEmpty$DataBuffer@1733c6a5
 [EmptyingLoop](After)com.luoluo.exchanger.FillAndEmpty$DataBuffer@39bcfec1
 ......

 

게시 된 136 개 원래 기사 · 원 찬양 6 · 전망 1532

추천

출처blog.csdn.net/weixin_42073629/article/details/104470510