Javaのマルチスレッドシリーズ - 「JUCロック」の原則と例の交換交換器12

交換のプロフィール

交換器が最初から用意JDK1.5からツールのスイートで、それは一般に2つのワーカースレッド間のデータ交換に使用されています。 

 定数紹介

     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、二つの溝7との間の実効アドレス長が<< 1(偽共有の問題を回避するために、キャッシュ・ラインの少なくともサイズ、下記参照します)

MMASK、マルチスロット交換が最大屈折率をサポートすることができ、サイズMMASK + 1(インデックスは0から始まります)

一意決定SEQ、結合インクリメントユニット、(高いです)

数NCPU、CPUの

FULLはなく、複数MMASKより最大アリーナ指数;アリーナ、スロットの列、良好な柔軟性を得るために、スロットを持つすべてのスレッドの競合を回避します。

SPINS、スピンウェイトのスピン周波数に、ウェイトはスピンに続いて、軽量であり、 - >収率 - >ブロック

 

ダミーは、局所性の原理によれば、交換データを、キャッシュメモリ及びキャッシュライン単位で共有されている間に、隣接するデータ・アドレス空間は、キャッシュ同一のデータブロック(キャッシュライン)にロードされ、配列は連続的です、メモリアドレス空間、したがって、スロットの複数の同一のキャッシュライン、スロット変化にロードされる(論理的には、仮想メモリに関連する)(他のキャッシュ・ラインを含むスロット上のデータのすべてを引き起こしますスロット)は、パフォーマンスに影響を与える、メモリから再ロードする必要が無効です。

したがって、このような状況を回避するために、データが有効にスロットが同じキャッシュラインにロードされていないのでこと、満たされる必要があります。図に示すように、充填材のサイズは、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; // 挂在此结点上阻塞着的线程
     }

インデックス、アリーナのインデックス

バインド、バインドされた最後のレコード

衝突は、右から左へ横切る、下限電流の最大実効屈折率のCAS障害、Mの最大値、M(結合&MMASK)下で結合の現在の数が、衝突== Mまで、実効屈折率は、スロットに横断しました、タイムスロットが成長する必要がある、成長モードがリセットされる結合(SEQ更新バージョン依存高く、; +1、低い)、リセット衝突一方

ハッシュ、擬似ランダム、スピンのために

アイテム、データは、現在のスレッドによって運ば

データを搬送する(交換スレッドに)一致、ストレージ解放スレッド

解放されるのを待っているスレッドをブロックし、この接合部に掛かって、停車中

下記を参照してください。

参加者のデータ構造

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

直接の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、0xFFの+ 1)+1後、例えばユニークな特性(、のバージョンを決定し、-1、実際リセットに結合し、衝突の2つのバージョンでありますそして、右からのインデックスの左越えにするためには、すぐに空の場所を見つけ、そして結合したとき、それを捕獲しようと、見つけることは右から左へ、右スロットの熾烈な競争のスロットよりも左に、一般的には、更新されるべきです我々は右のように更新するために、ほとんどの、一意のバージョンをバインドされていない場合は、何のインデックス更新が存在しない、となっているため、近い左側にあるので、空いたスロットの右側にあるべきインデックスをトラバースプラス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メソッドを呼び出し、その後、現在のスレッドが(割り込みフラグ)中断されたかどうかを判断する場合は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;
     }

概要

1.この待機のスレッドがすでに存在していることを示すスロットが空であるかどうかをチェック(ヌル)、空でない、占領の成功場合は、スロットを占有してみてください、とのデータ交換にスレッド待ち、スレッド、トランザクションの終了、復帰を待って目を覚まします。

2.スロットに障害が発生した占有する場合は、アリーナを作成しますが、スロットが空になるまで、スロットをつかむ、またはトランザクションの正常終了が返されるつかむしようとする[ステップ1]を継続します。

 

スロットが空の場合は3、アリーナアリーナがメソッドarenaExchangeに再ルーティング、空の戻りnullでない場合、空であるか否かを判断します

アリーナが空の場合は4、現在のスレッドが到着していることを示す、、、ループの外に、自分の職業のためのスロットをマークします成功した場合には、スロットを持つようにしようとステップ[5]に進み、それが失敗した場合、手順に進みます[1]

>利回り - - >ブロック失敗したCPUのタイムスライス(歩留まり)あきらめ、そして最終的には十分ではない(ブロック)をブロックする、スピンた場合に解放されるのを待って5現在のスレッドが、順番を待っていることは、最初のスピン(スピン)であります

タイムアウト(設定されたタイムアウトの場合)またはが中断された場合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)+ ABASEメモリ値、すなわちi番目の実際の使用可能なノード、それはスロットが、ステップ中に、空の空であるか否かが判断される[2]。 、スロットをつかむしようとし、この待機中のスレッドがあることを示す、空の成功をつかむ、交換データ、及び待機中のスレッドのリターンをウェイクアップ、終了していない、ステップ〜[9]に、成功をつかむしません

2.境界の、境界のうち、[ステップ9]に指数(I M対)をチェックし、しない限度で、次のステップ。

3. [ステップ1]入力し、グラブが失敗し、スロットを占有するようにしてください;、次のステップを成功をつかみます。

そうでない場合は、次のステップ;交換スレッドに、もしあれば、データ交換、最後にデータがある場合4、試合をチェックしてください。

ゼロより大きくない場合に前記空スピンは、次のステップ0より大きく、0より大きい場合、チェックハッシュは0以上であり、且つ0又はスピン半分であり、そうでない場合、ステップ[4]を入力し、それがある場合、そうCPUこと時間は、その後、[工程4]を入力します

6、mが最小値に達し、割込みをチェックタイムアウトが、中断なく、タイミング、および最小値Mに達したか否かを、ステップ[4]に、次いで、ブロックされ、そうでない場合、次のステップ。

再び開始する元のスロットを廃棄しようとしている交換データ7.なしスレッドは、[ステップ4]を入力することができなかった廃棄する;そうでない場合、次のステップ。

8.結合したマイナス1(m> 0)で、半分をインデックス、割り込みまたはタイムアウトかどうかを確認し、そうでない場合、ステップ[1]を入力し、そうでなければ、終了。

9.チェックは、リセット衝突を変更した場合、変更結合した場合、指数mがリセットまたは残され、ステアリング工程[1]、そうでない場合、次のステップ。

10.チェック衝突最大値に達し、そうでない場合、ステップ[13]、そうでなければ次のステップに入ります。

11 mがFULLに達し、[13]ステップで入力され、そうでない場合、次のステップ。

12. CASは、結合したプラスワンが成功し、成功した場合、IはM + 1に設定され、スロットの成長ステップ[1]を進め、そうでなければ、次のステップ。

13.衝突プラス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は、配列の要素は、アドレス空間のそれぞれによって占有されている、第一の配列要素ABASE偽シェアリングを防止する、オフセット

最後に、アリーナは=新しいノード説明[(FULL + 2)<< ASHIFT]、FULL、<= MMASK、スケール、<= 1 << ASHIFT、(FULL + 2)<< ASHIFTノードは、実際に利用可能FULL +です2、実際にFULL + 1番目、最後なしでの使用だけでなく、その権利及びませんが満たされたことを、他のデータ変更は、それに影響を与える可能性があり、最後に使用した場合、フォルス・シェアリングを防止するための、ある、の発生偽共有の問題。最大実効屈折率はMMASK(結合&MMASK)であるが、FULL、スロットを通る成長意志サイクルは、交換データしようとすると、M(実際の最大インデックス)が増加します。

擬似乱数

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

実際xorshiftアルゴリズム、T =(I + LA)左のL代表、右のRを表す、A、B、Cは、それぞれ、上記式3を表し、(I + RB)(I + LC)、 10、Iは{0,1}であり、32ビット(INT)、バイナリ整数の合計の行列を表す、ランダムアルゴリズムは、Tによって表されます 翻訳は、上記の式である:H ^ = H << 1; H ^ = H >>> 3; H ^ = H << 10。

なぜ我々は、1,3,10、それは使うべきでしょうか?

実際には、擬似乱数、真にランダムではなく、ランダムの効果を達成するためにシミュレートされたアルゴリズムによって、希望が大きく、より良いの期間です。時間まで、第1の入力の出力に等しく与えられた入力、およびその後等々次の入力として出力する、そして、こといわゆるサイクル手段サイクルのランダムアルゴリズムランダムな番号であることを起こります。この概念により、我々は、テスト対象のコードを書くことができます。

視覚的に(それが0である場合は常に0、ランダムに言及しないであろう、出力を0以外の、特異行列[])、INT型の最大期間はタイプのすべての値を横断する必要があり、推定であり、最大値 - 最小= 232 --1

Javaコード

 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)
...

それは丁度、(1,3,10)4294967295期間に発生し、最初の行で見ることができる232--1

同じ期間で表される基の複数の行。

質問、なぜ二人は左とその右側に1がありますか?実際には、一度だけまたは従属栄養がランダムな結果を達成することができます左。

初めて左おそらく、ので、その理由を推測し、より高よりもできるようにすることです、右、非常に高い、低いということが関係している、複数の低できるようにすることです、ランダム性を高め、左から二番目、それが真にランダムです。

例として交換 

二つのスレッド、二つのバッファ、データバッファが充填されたスレッドは、別のスレッドは、他のバッファ内からデータをフェッチします。スレッドが充填されているバッファデータを埋める、又はデータの糸を取るときに、データバッファを取る空である、それは他の交換バッファにアクションを開始し、交換のタイミングは、他のバッファが満杯であることですバッファが空です。次のコードは何の脚注がない、非常に簡単です。

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