1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /* 26 * This file is available under and governed by the GNU General Public 27 * License version 2 only, as published by the Free Software Foundation. 28 * However, the following notice accompanied the original version of this 29 * file: 30 * 31 * Written by Doug Lea, Bill Scherer, and Michael Scott with 32 * assistance from members of JCP JSR-166 Expert Group and released to 33 * the public domain, as explained at 34 * http://creativecommons.org/publicdomain/zero/1.0/ 35 */ 36 37 package java.util.concurrent; 38 39 import java.lang.invoke.MethodHandles; 40 import java.lang.invoke.VarHandle; 41 import java.util.concurrent.locks.LockSupport; 42 43 /** 44 * A synchronization point at which threads can pair and swap elements 45 * within pairs. Each thread presents some object on entry to the 46 * {@link #exchange exchange} method, matches with a partner thread, 47 * and receives its partner's object on return. An Exchanger may be 48 * viewed as a bidirectional form of a {@link SynchronousQueue}. 49 * Exchangers may be useful in applications such as genetic algorithms 50 * and pipeline designs. 51 * 52 * <p><b>Sample Usage:</b> 53 * Here are the highlights of a class that uses an {@code Exchanger} 54 * to swap buffers between threads so that the thread filling the 55 * buffer gets a freshly emptied one when it needs it, handing off the 56 * filled one to the thread emptying the buffer. 57 * <pre> {@code 58 * class FillAndEmpty { 59 * Exchanger<DataBuffer> exchanger = new Exchanger<>(); 60 * DataBuffer initialEmptyBuffer = ...; // a made-up type 61 * DataBuffer initialFullBuffer = ...; 62 * 63 * class FillingLoop implements Runnable { 64 * public void run() { 65 * DataBuffer currentBuffer = initialEmptyBuffer; 66 * try { 67 * while (currentBuffer != null) { 68 * addToBuffer(currentBuffer); 69 * if (currentBuffer.isFull()) 70 * currentBuffer = exchanger.exchange(currentBuffer); 71 * } 72 * } catch (InterruptedException ex) { ... handle ...} 73 * } 74 * } 75 * 76 * class EmptyingLoop implements Runnable { 77 * public void run() { 78 * DataBuffer currentBuffer = initialFullBuffer; 79 * try { 80 * while (currentBuffer != null) { 81 * takeFromBuffer(currentBuffer); 82 * if (currentBuffer.isEmpty()) 83 * currentBuffer = exchanger.exchange(currentBuffer); 84 * } 85 * } catch (InterruptedException ex) { ... handle ...} 86 * } 87 * } 88 * 89 * void start() { 90 * new Thread(new FillingLoop()).start(); 91 * new Thread(new EmptyingLoop()).start(); 92 * } 93 * }}</pre> 94 * 95 * <p>Memory consistency effects: For each pair of threads that 96 * successfully exchange objects via an {@code Exchanger}, actions 97 * prior to the {@code exchange()} in each thread 98 * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> 99 * those subsequent to a return from the corresponding {@code exchange()} 100 * in the other thread. 101 * 102 * @since 1.5 103 * @author Doug Lea and Bill Scherer and Michael Scott 104 * @param <V> The type of objects that may be exchanged 105 */ 106 public class Exchanger<V> { 107 108 /* 109 * Overview: The core algorithm is, for an exchange "slot", 110 * and a participant (caller) with an item: 111 * 112 * for (;;) { 113 * if (slot is empty) { // offer 114 * place item in a Node; 115 * if (can CAS slot from empty to node) { 116 * wait for release; 117 * return matching item in node; 118 * } 119 * } 120 * else if (can CAS slot from node to empty) { // release 121 * get the item in node; 122 * set matching item in node; 123 * release waiting thread; 124 * } 125 * // else retry on CAS failure 126 * } 127 * 128 * This is among the simplest forms of a "dual data structure" -- 129 * see Scott and Scherer's DISC 04 paper and 130 * http://www.cs.rochester.edu/research/synchronization/pseudocode/duals.html 131 * 132 * This works great in principle. But in practice, like many 133 * algorithms centered on atomic updates to a single location, it 134 * scales horribly when there are more than a few participants 135 * using the same Exchanger. So the implementation instead uses a 136 * form of elimination arena, that spreads out this contention by 137 * arranging that some threads typically use different slots, 138 * while still ensuring that eventually, any two parties will be 139 * able to exchange items. That is, we cannot completely partition 140 * across threads, but instead give threads arena indices that 141 * will on average grow under contention and shrink under lack of 142 * contention. We approach this by defining the Nodes that we need 143 * anyway as ThreadLocals, and include in them per-thread index 144 * and related bookkeeping state. (We can safely reuse per-thread 145 * nodes rather than creating them fresh each time because slots 146 * alternate between pointing to a node vs null, so cannot 147 * encounter ABA problems. However, we do need some care in 148 * resetting them between uses.) 149 * 150 * Implementing an effective arena requires allocating a bunch of 151 * space, so we only do so upon detecting contention (except on 152 * uniprocessors, where they wouldn't help, so aren't used). 153 * Otherwise, exchanges use the single-slot slotExchange method. 154 * On contention, not only must the slots be in different 155 * locations, but the locations must not encounter memory 156 * contention due to being on the same cache line (or more 157 * generally, the same coherence unit). Because, as of this 158 * writing, there is no way to determine cacheline size, we define 159 * a value that is enough for common platforms. Additionally, 160 * extra care elsewhere is taken to avoid other false/unintended 161 * sharing and to enhance locality, including adding padding (via 162 * @Contended) to Nodes, embedding "bound" as an Exchanger field. 163 * 164 * The arena starts out with only one used slot. We expand the 165 * effective arena size by tracking collisions; i.e., failed CASes 166 * while trying to exchange. By nature of the above algorithm, the 167 * only kinds of collision that reliably indicate contention are 168 * when two attempted releases collide -- one of two attempted 169 * offers can legitimately fail to CAS without indicating 170 * contention by more than one other thread. (Note: it is possible 171 * but not worthwhile to more precisely detect contention by 172 * reading slot values after CAS failures.) When a thread has 173 * collided at each slot within the current arena bound, it tries 174 * to expand the arena size by one. We track collisions within 175 * bounds by using a version (sequence) number on the "bound" 176 * field, and conservatively reset collision counts when a 177 * participant notices that bound has been updated (in either 178 * direction). 179 * 180 * The effective arena size is reduced (when there is more than 181 * one slot) by giving up on waiting after a while and trying to 182 * decrement the arena size on expiration. The value of "a while" 183 * is an empirical matter. We implement by piggybacking on the 184 * use of spin->yield->block that is essential for reasonable 185 * waiting performance anyway -- in a busy exchanger, offers are 186 * usually almost immediately released, in which case context 187 * switching on multiprocessors is extremely slow/wasteful. Arena 188 * waits just omit the blocking part, and instead cancel. The spin 189 * count is empirically chosen to be a value that avoids blocking 190 * 99% of the time under maximum sustained exchange rates on a 191 * range of test machines. Spins and yields entail some limited 192 * randomness (using a cheap xorshift) to avoid regular patterns 193 * that can induce unproductive grow/shrink cycles. (Using a 194 * pseudorandom also helps regularize spin cycle duration by 195 * making branches unpredictable.) Also, during an offer, a 196 * waiter can "know" that it will be released when its slot has 197 * changed, but cannot yet proceed until match is set. In the 198 * mean time it cannot cancel the offer, so instead spins/yields. 199 * Note: It is possible to avoid this secondary check by changing 200 * the linearization point to be a CAS of the match field (as done 201 * in one case in the Scott & Scherer DISC paper), which also 202 * increases asynchrony a bit, at the expense of poorer collision 203 * detection and inability to always reuse per-thread nodes. So 204 * the current scheme is typically a better tradeoff. 205 * 206 * On collisions, indices traverse the arena cyclically in reverse 207 * order, restarting at the maximum index (which will tend to be 208 * sparsest) when bounds change. (On expirations, indices instead 209 * are halved until reaching 0.) It is possible (and has been 210 * tried) to use randomized, prime-value-stepped, or double-hash 211 * style traversal instead of simple cyclic traversal to reduce 212 * bunching. But empirically, whatever benefits these may have 213 * don't overcome their added overhead: We are managing operations 214 * that occur very quickly unless there is sustained contention, 215 * so simpler/faster control policies work better than more 216 * accurate but slower ones. 217 * 218 * Because we use expiration for arena size control, we cannot 219 * throw TimeoutExceptions in the timed version of the public 220 * exchange method until the arena size has shrunken to zero (or 221 * the arena isn't enabled). This may delay response to timeout 222 * but is still within spec. 223 * 224 * Essentially all of the implementation is in methods 225 * slotExchange and arenaExchange. These have similar overall 226 * structure, but differ in too many details to combine. The 227 * slotExchange method uses the single Exchanger field "slot" 228 * rather than arena array elements. However, it still needs 229 * minimal collision detection to trigger arena construction. 230 * (The messiest part is making sure interrupt status and 231 * InterruptedExceptions come out right during transitions when 232 * both methods may be called. This is done by using null return 233 * as a sentinel to recheck interrupt status.) 234 * 235 * As is too common in this sort of code, methods are monolithic 236 * because most of the logic relies on reads of fields that are 237 * maintained as local variables so can't be nicely factored -- 238 * mainly, here, bulky spin->yield->block/cancel code. Note that 239 * field Node.item is not declared as volatile even though it is 240 * read by releasing threads, because they only do so after CAS 241 * operations that must precede access, and all uses by the owning 242 * thread are otherwise acceptably ordered by other operations. 243 * (Because the actual points of atomicity are slot CASes, it 244 * would also be legal for the write to Node.match in a release to 245 * be weaker than a full volatile write. However, this is not done 246 * because it could allow further postponement of the write, 247 * delaying progress.) 248 */ 249 250 /** 251 * The index distance (as a shift value) between any two used slots 252 * in the arena, spacing them out to avoid false sharing. 253 */ 254 private static final int ASHIFT = 5; 255 256 /** 257 * The maximum supported arena index. The maximum allocatable 258 * arena size is MMASK + 1. Must be a power of two minus one, less 259 * than (1<<(31-ASHIFT)). The cap of 255 (0xff) more than suffices 260 * for the expected scaling limits of the main algorithms. 261 */ 262 private static final int MMASK = 0xff; 263 264 /** 265 * Unit for sequence/version bits of bound field. Each successful 266 * change to the bound also adds SEQ. 267 */ 268 private static final int SEQ = MMASK + 1; 269 270 /** The number of CPUs, for sizing and spin control */ 271 private static final int NCPU = Runtime.getRuntime().availableProcessors(); 272 273 /** 274 * The maximum slot index of the arena: The number of slots that 275 * can in principle hold all threads without contention, or at 276 * most the maximum indexable value. 277 */ 278 static final int FULL = (NCPU >= (MMASK << 1)) ? MMASK : NCPU >>> 1; 279 280 /** 281 * The bound for spins while waiting for a match. The actual 282 * number of iterations will on average be about twice this value 283 * due to randomization. Note: Spinning is disabled when NCPU==1. 284 */ 285 private static final int SPINS = 1 << 10; 286 287 /** 288 * Value representing null arguments/returns from public 289 * methods. Needed because the API originally didn't disallow null 290 * arguments, which it should have. 291 */ 292 private static final Object NULL_ITEM = new Object(); 293 294 /** 295 * Sentinel value returned by internal exchange methods upon 296 * timeout, to avoid need for separate timed versions of these 297 * methods. 298 */ 299 private static final Object TIMED_OUT = new Object(); 300 301 /** 302 * Nodes hold partially exchanged data, plus other per-thread 303 * bookkeeping. Padded via @Contended to reduce memory contention. 304 */ 305 @jdk.internal.vm.annotation.Contended 306 static final class Node { 307 int index; // Arena index 308 int bound; // Last recorded value of Exchanger.bound 309 int collides; // Number of CAS failures at current bound 310 int hash; // Pseudo-random for spins 311 Object item; // This thread's current item 312 volatile Object match; // Item provided by releasing thread 313 volatile Thread parked; // Set to this thread when parked, else null 314 } 315 316 /** The corresponding thread local class */ 317 static final class Participant extends ThreadLocal<Node> { initialValue()318 public Node initialValue() { return new Node(); } 319 } 320 321 /** 322 * Per-thread state. 323 */ 324 private final Participant participant; 325 326 /** 327 * Elimination array; null until enabled (within slotExchange). 328 * Element accesses use emulation of volatile gets and CAS. 329 */ 330 private volatile Node[] arena; 331 332 /** 333 * Slot used until contention detected. 334 */ 335 private volatile Node slot; 336 337 /** 338 * The index of the largest valid arena position, OR'ed with SEQ 339 * number in high bits, incremented on each update. The initial 340 * update from 0 to SEQ is used to ensure that the arena array is 341 * constructed only once. 342 */ 343 private volatile int bound; 344 345 /** 346 * Exchange function when arenas enabled. See above for explanation. 347 * 348 * @param item the (non-null) item to exchange 349 * @param timed true if the wait is timed 350 * @param ns if timed, the maximum wait time, else 0L 351 * @return the other thread's item; or null if interrupted; or 352 * TIMED_OUT if timed and timed out 353 */ arenaExchange(Object item, boolean timed, long ns)354 private final Object arenaExchange(Object item, boolean timed, long ns) { 355 Node[] a = arena; 356 int alen = a.length; 357 Node p = participant.get(); 358 for (int i = p.index;;) { // access slot at i 359 int b, m, c; 360 int j = (i << ASHIFT) + ((1 << ASHIFT) - 1); 361 if (j < 0 || j >= alen) 362 j = alen - 1; 363 Node q = (Node)AA.getAcquire(a, j); 364 if (q != null && AA.compareAndSet(a, j, q, null)) { 365 Object v = q.item; // release 366 q.match = item; 367 Thread w = q.parked; 368 if (w != null) 369 LockSupport.unpark(w); 370 return v; 371 } 372 else if (i <= (m = (b = bound) & MMASK) && q == null) { 373 p.item = item; // offer 374 if (AA.compareAndSet(a, j, null, p)) { 375 long end = (timed && m == 0) ? System.nanoTime() + ns : 0L; 376 Thread t = Thread.currentThread(); // wait 377 for (int h = p.hash, spins = SPINS;;) { 378 Object v = p.match; 379 if (v != null) { 380 MATCH.setRelease(p, null); 381 p.item = null; // clear for next use 382 p.hash = h; 383 return v; 384 } 385 else if (spins > 0) { 386 h ^= h << 1; h ^= h >>> 3; h ^= h << 10; // xorshift 387 if (h == 0) // initialize hash 388 h = SPINS | (int)t.getId(); 389 else if (h < 0 && // approx 50% true 390 (--spins & ((SPINS >>> 1) - 1)) == 0) 391 Thread.yield(); // two yields per wait 392 } 393 else if (AA.getAcquire(a, j) != p) 394 spins = SPINS; // releaser hasn't set match yet 395 else if (!t.isInterrupted() && m == 0 && 396 (!timed || 397 (ns = end - System.nanoTime()) > 0L)) { 398 p.parked = t; // minimize window 399 if (AA.getAcquire(a, j) == p) { 400 if (ns == 0L) 401 LockSupport.park(this); 402 else 403 LockSupport.parkNanos(this, ns); 404 } 405 p.parked = null; 406 } 407 else if (AA.getAcquire(a, j) == p && 408 AA.compareAndSet(a, j, p, null)) { 409 if (m != 0) // try to shrink 410 BOUND.compareAndSet(this, b, b + SEQ - 1); 411 p.item = null; 412 p.hash = h; 413 i = p.index >>>= 1; // descend 414 if (Thread.interrupted()) 415 return null; 416 if (timed && m == 0 && ns <= 0L) 417 return TIMED_OUT; 418 break; // expired; restart 419 } 420 } 421 } 422 else 423 p.item = null; // clear offer 424 } 425 else { 426 if (p.bound != b) { // stale; reset 427 p.bound = b; 428 p.collides = 0; 429 i = (i != m || m == 0) ? m : m - 1; 430 } 431 else if ((c = p.collides) < m || m == FULL || 432 !BOUND.compareAndSet(this, b, b + SEQ + 1)) { 433 p.collides = c + 1; 434 i = (i == 0) ? m : i - 1; // cyclically traverse 435 } 436 else 437 i = m + 1; // grow 438 p.index = i; 439 } 440 } 441 } 442 443 /** 444 * Exchange function used until arenas enabled. See above for explanation. 445 * 446 * @param item the item to exchange 447 * @param timed true if the wait is timed 448 * @param ns if timed, the maximum wait time, else 0L 449 * @return the other thread's item; or null if either the arena 450 * was enabled or the thread was interrupted before completion; or 451 * TIMED_OUT if timed and timed out 452 */ slotExchange(Object item, boolean timed, long ns)453 private final Object slotExchange(Object item, boolean timed, long ns) { 454 Node p = participant.get(); 455 Thread t = Thread.currentThread(); 456 if (t.isInterrupted()) // preserve interrupt status so caller can recheck 457 return null; 458 459 for (Node q;;) { 460 if ((q = slot) != null) { 461 if (SLOT.compareAndSet(this, q, null)) { 462 Object v = q.item; 463 q.match = item; 464 Thread w = q.parked; 465 if (w != null) 466 LockSupport.unpark(w); 467 return v; 468 } 469 // create arena on contention, but continue until slot null 470 if (NCPU > 1 && bound == 0 && 471 BOUND.compareAndSet(this, 0, SEQ)) 472 arena = new Node[(FULL + 2) << ASHIFT]; 473 } 474 else if (arena != null) 475 return null; // caller must reroute to arenaExchange 476 else { 477 p.item = item; 478 if (SLOT.compareAndSet(this, null, p)) 479 break; 480 p.item = null; 481 } 482 } 483 484 // await release 485 int h = p.hash; 486 long end = timed ? System.nanoTime() + ns : 0L; 487 int spins = (NCPU > 1) ? SPINS : 1; 488 Object v; 489 while ((v = p.match) == null) { 490 if (spins > 0) { 491 h ^= h << 1; h ^= h >>> 3; h ^= h << 10; 492 if (h == 0) 493 h = SPINS | (int)t.getId(); 494 else if (h < 0 && (--spins & ((SPINS >>> 1) - 1)) == 0) 495 Thread.yield(); 496 } 497 else if (slot != p) 498 spins = SPINS; 499 else if (!t.isInterrupted() && arena == null && 500 (!timed || (ns = end - System.nanoTime()) > 0L)) { 501 p.parked = t; 502 if (slot == p) { 503 if (ns == 0L) 504 LockSupport.park(this); 505 else 506 LockSupport.parkNanos(this, ns); 507 } 508 p.parked = null; 509 } 510 else if (SLOT.compareAndSet(this, p, null)) { 511 v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null; 512 break; 513 } 514 } 515 MATCH.setRelease(p, null); 516 p.item = null; 517 p.hash = h; 518 return v; 519 } 520 521 /** 522 * Creates a new Exchanger. 523 */ Exchanger()524 public Exchanger() { 525 participant = new Participant(); 526 } 527 528 /** 529 * Waits for another thread to arrive at this exchange point (unless 530 * the current thread is {@linkplain Thread#interrupt interrupted}), 531 * and then transfers the given object to it, receiving its object 532 * in return. 533 * 534 * <p>If another thread is already waiting at the exchange point then 535 * it is resumed for thread scheduling purposes and receives the object 536 * passed in by the current thread. The current thread returns immediately, 537 * receiving the object passed to the exchange by that other thread. 538 * 539 * <p>If no other thread is already waiting at the exchange then the 540 * current thread is disabled for thread scheduling purposes and lies 541 * dormant until one of two things happens: 542 * <ul> 543 * <li>Some other thread enters the exchange; or 544 * <li>Some other thread {@linkplain Thread#interrupt interrupts} 545 * the current thread. 546 * </ul> 547 * <p>If the current thread: 548 * <ul> 549 * <li>has its interrupted status set on entry to this method; or 550 * <li>is {@linkplain Thread#interrupt interrupted} while waiting 551 * for the exchange, 552 * </ul> 553 * then {@link InterruptedException} is thrown and the current thread's 554 * interrupted status is cleared. 555 * 556 * @param x the object to exchange 557 * @return the object provided by the other thread 558 * @throws InterruptedException if the current thread was 559 * interrupted while waiting 560 */ 561 @SuppressWarnings("unchecked") exchange(V x)562 public V exchange(V x) throws InterruptedException { 563 Object v; 564 Node[] a; 565 Object item = (x == null) ? NULL_ITEM : x; // translate null args 566 if (((a = arena) != null || 567 (v = slotExchange(item, false, 0L)) == null) && 568 (Thread.interrupted() || // disambiguates null return 569 (v = arenaExchange(item, false, 0L)) == null)) 570 throw new InterruptedException(); 571 return (v == NULL_ITEM) ? null : (V)v; 572 } 573 574 /** 575 * Waits for another thread to arrive at this exchange point (unless 576 * the current thread is {@linkplain Thread#interrupt interrupted} or 577 * the specified waiting time elapses), and then transfers the given 578 * object to it, receiving its object in return. 579 * 580 * <p>If another thread is already waiting at the exchange point then 581 * it is resumed for thread scheduling purposes and receives the object 582 * passed in by the current thread. The current thread returns immediately, 583 * receiving the object passed to the exchange by that other thread. 584 * 585 * <p>If no other thread is already waiting at the exchange then the 586 * current thread is disabled for thread scheduling purposes and lies 587 * dormant until one of three things happens: 588 * <ul> 589 * <li>Some other thread enters the exchange; or 590 * <li>Some other thread {@linkplain Thread#interrupt interrupts} 591 * the current thread; or 592 * <li>The specified waiting time elapses. 593 * </ul> 594 * <p>If the current thread: 595 * <ul> 596 * <li>has its interrupted status set on entry to this method; or 597 * <li>is {@linkplain Thread#interrupt interrupted} while waiting 598 * for the exchange, 599 * </ul> 600 * then {@link InterruptedException} is thrown and the current thread's 601 * interrupted status is cleared. 602 * 603 * <p>If the specified waiting time elapses then {@link 604 * TimeoutException} is thrown. If the time is less than or equal 605 * to zero, the method will not wait at all. 606 * 607 * @param x the object to exchange 608 * @param timeout the maximum time to wait 609 * @param unit the time unit of the {@code timeout} argument 610 * @return the object provided by the other thread 611 * @throws InterruptedException if the current thread was 612 * interrupted while waiting 613 * @throws TimeoutException if the specified waiting time elapses 614 * before another thread enters the exchange 615 */ 616 @SuppressWarnings("unchecked") exchange(V x, long timeout, TimeUnit unit)617 public V exchange(V x, long timeout, TimeUnit unit) 618 throws InterruptedException, TimeoutException { 619 Object v; 620 Object item = (x == null) ? NULL_ITEM : x; 621 long ns = unit.toNanos(timeout); 622 if ((arena != null || 623 (v = slotExchange(item, true, ns)) == null) && 624 (Thread.interrupted() || 625 (v = arenaExchange(item, true, ns)) == null)) 626 throw new InterruptedException(); 627 if (v == TIMED_OUT) 628 throw new TimeoutException(); 629 return (v == NULL_ITEM) ? null : (V)v; 630 } 631 632 // VarHandle mechanics 633 private static final VarHandle BOUND; 634 private static final VarHandle SLOT; 635 private static final VarHandle MATCH; 636 private static final VarHandle AA; 637 static { 638 try { 639 MethodHandles.Lookup l = MethodHandles.lookup(); 640 BOUND = l.findVarHandle(Exchanger.class, "bound", int.class); 641 SLOT = l.findVarHandle(Exchanger.class, "slot", Node.class); 642 MATCH = l.findVarHandle(Node.class, "match", Object.class); 643 AA = MethodHandles.arrayElementVarHandle(Node[].class); 644 } catch (ReflectiveOperationException e) { 645 throw new ExceptionInInitializerError(e); 646 } 647 } 648 649 } 650