Java程序  |  382行  |  10.42 KB

/*
 * Written by Doug Lea and Martin Buchholz with assistance from
 * members of JCP JSR-166 Expert Group and released to the public
 * domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

/*
 * Source:
 * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/tck-jsr166e/AtomicDoubleArrayTest.java?revision=1.13
 * (Modified to adapt to guava coding conventions)
 */

package com.google.common.util.concurrent;

import junit.framework.*;
import java.util.Arrays;

/**
 * Unit test for {@link AtomicDoubleArray}.
 */
public class AtomicDoubleArrayTest extends JSR166TestCase {

  private static final double[] VALUES = {
    Double.NEGATIVE_INFINITY,
    -Double.MAX_VALUE,
    (double) Long.MIN_VALUE,
    (double) Integer.MIN_VALUE,
    -Math.PI,
    -1.0,
    -Double.MIN_VALUE,
    -0.0,
    +0.0,
    Double.MIN_VALUE,
    1.0,
    Math.PI,
    (double) Integer.MAX_VALUE,
    (double) Long.MAX_VALUE,
    Double.MAX_VALUE,
    Double.POSITIVE_INFINITY,
    Double.NaN,
    Float.MAX_VALUE,
  };

  /** The notion of equality used by AtomicDoubleArray */
  static boolean bitEquals(double x, double y) {
    return Double.doubleToRawLongBits(x) == Double.doubleToRawLongBits(y);
  }

  static void assertBitEquals(double x, double y) {
    assertEquals(Double.doubleToRawLongBits(x),
                 Double.doubleToRawLongBits(y));
  }

  /**
   * constructor creates array of given size with all elements zero
   */
  public void testConstructor() {
    AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
    for (int i = 0; i < SIZE; i++) {
      assertBitEquals(0.0, aa.get(i));
    }
  }

  /**
   * constructor with null array throws NPE
   */
  public void testConstructor2NPE() {
    try {
      double[] a = null;
      AtomicDoubleArray aa = new AtomicDoubleArray(a);
      shouldThrow();
    } catch (NullPointerException success) {}
  }

  /**
   * constructor with array is of same size and has all elements
   */
  public void testConstructor2() {
    AtomicDoubleArray aa = new AtomicDoubleArray(VALUES);
    assertEquals(VALUES.length, aa.length());
    for (int i = 0; i < VALUES.length; i++) {
      assertBitEquals(VALUES[i], aa.get(i));
    }
  }

  /**
   * constructor with empty array has size 0 and contains no elements
   */
  public void testConstructorEmptyArray() {
    AtomicDoubleArray aa = new AtomicDoubleArray(new double[0]);
    assertEquals(0, aa.length());
    try {
      aa.get(0);
      shouldThrow();
    } catch (IndexOutOfBoundsException success) {}
  }

  /**
   * constructor with length zero has size 0 and contains no elements
   */
  public void testConstructorZeroLength() {
    AtomicDoubleArray aa = new AtomicDoubleArray(0);
    assertEquals(0, aa.length());
    try {
      aa.get(0);
      shouldThrow();
    } catch (IndexOutOfBoundsException success) {}
  }

  /**
   * get and set for out of bound indices throw IndexOutOfBoundsException
   */
  public void testIndexing() {
    AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
    for (int index : new int[] { -1, SIZE }) {
      try {
        aa.get(index);
        shouldThrow();
      } catch (IndexOutOfBoundsException success) {}
      try {
        aa.set(index, 1.0);
        shouldThrow();
      } catch (IndexOutOfBoundsException success) {}
      try {
        aa.lazySet(index, 1.0);
        shouldThrow();
      } catch (IndexOutOfBoundsException success) {}
      try {
        aa.compareAndSet(index, 1.0, 2.0);
        shouldThrow();
      } catch (IndexOutOfBoundsException success) {}
      try {
        aa.weakCompareAndSet(index, 1.0, 2.0);
        shouldThrow();
      } catch (IndexOutOfBoundsException success) {}
      try {
        aa.getAndAdd(index, 1.0);
        shouldThrow();
      } catch (IndexOutOfBoundsException success) {}
      try {
        aa.addAndGet(index, 1.0);
        shouldThrow();
      } catch (IndexOutOfBoundsException success) {}
    }
  }

  /**
   * get returns the last value set at index
   */
  public void testGetSet() {
    AtomicDoubleArray aa = new AtomicDoubleArray(VALUES.length);
    for (int i = 0; i < VALUES.length; i++) {
      assertBitEquals(0.0, aa.get(i));
      aa.set(i, VALUES[i]);
      assertBitEquals(VALUES[i], aa.get(i));
      aa.set(i, -3.0);
      assertBitEquals(-3.0, aa.get(i));
    }
  }

  /**
   * get returns the last value lazySet at index by same thread
   */
  public void testGetLazySet() {
    AtomicDoubleArray aa = new AtomicDoubleArray(VALUES.length);
    for (int i = 0; i < VALUES.length; i++) {
      assertBitEquals(0.0, aa.get(i));
      aa.lazySet(i, VALUES[i]);
      assertBitEquals(VALUES[i], aa.get(i));
      aa.lazySet(i, -3.0);
      assertBitEquals(-3.0, aa.get(i));
    }
  }

  /**
   * compareAndSet succeeds in changing value if equal to expected else fails
   */
  public void testCompareAndSet() {
    AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
    for (int i : new int[] { 0, SIZE - 1}) {
      double prev = 0.0;
      double unused = Math.E + Math.PI;
      for (double x : VALUES) {
        assertBitEquals(prev, aa.get(i));
        assertFalse(aa.compareAndSet(i, unused, x));
        assertBitEquals(prev, aa.get(i));
        assertTrue(aa.compareAndSet(i, prev, x));
        assertBitEquals(x, aa.get(i));
        prev = x;
      }
    }
  }

  /**
   * compareAndSet in one thread enables another waiting for value
   * to succeed
   */

      public void testCompareAndSetInMultipleThreads() throws InterruptedException {
    final AtomicDoubleArray a = new AtomicDoubleArray(1);
    a.set(0, 1.0);
    Thread t = newStartedThread(new CheckedRunnable() {
        public void realRun() {
          while (!a.compareAndSet(0, 2.0, 3.0)) {
            Thread.yield();
          }
        }});

    assertTrue(a.compareAndSet(0, 1.0, 2.0));
    awaitTermination(t);
    assertBitEquals(3.0, a.get(0));
  }

  /**
   * repeated weakCompareAndSet succeeds in changing value when equal
   * to expected
   */
  public void testWeakCompareAndSet() {
    AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
    for (int i : new int[] { 0, SIZE - 1}) {
      double prev = 0.0;
      double unused = Math.E + Math.PI;
      for (double x : VALUES) {
        assertBitEquals(prev, aa.get(i));
        assertFalse(aa.weakCompareAndSet(i, unused, x));
        assertBitEquals(prev, aa.get(i));
        while (!aa.weakCompareAndSet(i, prev, x)) {
          ;
        }
        assertBitEquals(x, aa.get(i));
        prev = x;
      }
    }
  }

  /**
   * getAndSet returns previous value and sets to given value at given index
   */
  public void testGetAndSet() {
    AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
    for (int i : new int[] { 0, SIZE - 1}) {
      double prev = 0.0;
      for (double x : VALUES) {
        assertBitEquals(prev, aa.getAndSet(i, x));
        prev = x;
      }
    }
  }

  /**
   * getAndAdd returns previous value and adds given value
   */
  public void testGetAndAdd() {
    AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
    for (int i : new int[] { 0, SIZE - 1}) {
      for (double x : VALUES) {
        for (double y : VALUES) {
          aa.set(i, x);
          double z = aa.getAndAdd(i, y);
          assertBitEquals(x, z);
          assertBitEquals(x + y, aa.get(i));
        }
      }
    }
  }

  /**
   * addAndGet adds given value to current, and returns current value
   */
  public void testAddAndGet() {
    AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
    for (int i : new int[] { 0, SIZE - 1}) {
      for (double x : VALUES) {
        for (double y : VALUES) {
          aa.set(i, x);
          double z = aa.addAndGet(i, y);
          assertBitEquals(x + y, z);
          assertBitEquals(x + y, aa.get(i));
        }
      }
    }
  }

  static final long COUNTDOWN = 100000;

  class Counter extends CheckedRunnable {
    final AtomicDoubleArray aa;
    volatile long counts;
    Counter(AtomicDoubleArray a) { aa = a; }
    public void realRun() {
      for (;;) {
        boolean done = true;
        for (int i = 0; i < aa.length(); i++) {
          double v = aa.get(i);
          assertTrue(v >= 0);
          if (v != 0) {
            done = false;
            if (aa.compareAndSet(i, v, v - 1.0)) {
              ++counts;
            }
          }
        }
        if (done) {
          break;
        }
      }
    }
  }

  /**
   * Multiple threads using same array of counters successfully
   * update a number of times equal to total count
   */

      public void testCountingInMultipleThreads() throws InterruptedException {
    final AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
    for (int i = 0; i < SIZE; i++) {
      aa.set(i, (double) COUNTDOWN);
    }
    Counter c1 = new Counter(aa);
    Counter c2 = new Counter(aa);
    Thread t1 = newStartedThread(c1);
    Thread t2 = newStartedThread(c2);
    awaitTermination(t1);
    awaitTermination(t2);
    assertEquals(c1.counts + c2.counts, SIZE * COUNTDOWN);
  }

  /**
   * a deserialized serialized array holds same values
   */
  public void testSerialization() throws Exception {
    AtomicDoubleArray x = new AtomicDoubleArray(SIZE);
    for (int i = 0; i < SIZE; i++) {
      x.set(i, (double) -i);
    }
    AtomicDoubleArray y = serialClone(x);
    assertTrue(x != y);
    assertEquals(x.length(), y.length());
    for (int i = 0; i < SIZE; i++) {
      assertBitEquals(x.get(i), y.get(i));
    }

    AtomicDoubleArray a = new AtomicDoubleArray(VALUES);
    AtomicDoubleArray b = serialClone(a);
    assertFalse(a.equals(b));
    assertFalse(b.equals(a));
    assertEquals(a.length(), b.length());
    for (int i = 0; i < VALUES.length; i++) {
      assertBitEquals(a.get(i), b.get(i));
    }
  }

  /**
   * toString returns current value
   */
  public void testToString() {
    AtomicDoubleArray aa = new AtomicDoubleArray(VALUES);
    assertEquals(Arrays.toString(VALUES), aa.toString());
    assertEquals("[]", new AtomicDoubleArray(0).toString());
    assertEquals("[]", new AtomicDoubleArray(new double[0]).toString());
  }

  /**
   * compareAndSet treats +0.0 and -0.0 as distinct values
   */
  public void testDistinctZeros() {
    AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
    for (int i : new int[] { 0, SIZE - 1}) {
      assertFalse(aa.compareAndSet(i, -0.0, 7.0));
      assertFalse(aa.weakCompareAndSet(i, -0.0, 7.0));
      assertBitEquals(+0.0, aa.get(i));
      assertTrue(aa.compareAndSet(i, +0.0, -0.0));
      assertBitEquals(-0.0, aa.get(i));
      assertFalse(aa.compareAndSet(i, +0.0, 7.0));
      assertFalse(aa.weakCompareAndSet(i, +0.0, 7.0));
      assertBitEquals(-0.0, aa.get(i));
    }
  }
}