2017-10-28 38 views
0

我现在阅读在Java中思考关于原子性和可见性的章节。有一个我不明白的例子。Java可见性和同步 - Java示例中的思考

public class SerialNumberGenerator { 
    private static volatile int serialNumber = 0; 

    public static int nextSerialNumber() { 
     return serialNumber++; 
    } 
} 

class CircularSet { 
    private int[] array; 
    private int len; 
    private int index = 0; 

public CircularSet(int size) { 
    array = new int[size]; 
    len = size; 
    for (int i = 0; i < size; i++) { 
     array[i] = -1; 
    } 
} 

synchronized void add(int i) { 
    array[index] = i; 
    index = ++index % len; 
} 

synchronized boolean contains(int val) { 
    for (int i = 0; i < len; i++) { 
     if (array[i] == val) 
      return true; 
    } 
    return false; 
} 

}

公共类SerialNumberChecker {

private static final int SIZE = 10; 
private static CircularSet serials = new CircularSet(1000); 
private static ExecutorService exec = Executors.newCachedThreadPool(); 

static class SerialChecker implements Runnable { 

    @Override 
    public void run() { 
     while (true) { 
      int serial = SerialNumberGenerator.nextSerialNumber(); 
      if (serials.contains(serial)) { 
       System.out.println("Duplicate: " + serial); 
       System.exit(0); 
      } 
      serials.add(serial); 
     } 
    } 
} 

public static void main(String[] args) throws Exception { 
    for (int i = 0; i < SIZE; i++) { 
     exec.execute(new SerialChecker()); 
    } 
} 

}

输出示例:

Duplicate: 228 

我不明白这怎么可能。即使方法nextSerialNumber()也不同步,并且所有线程都会生成不同的值,每个线程都有自己的序列值,每个值都不相同。那么怎么可能找到重复的。我无法想象线程执行。

回答

1

此示例显示增量后运算符不是原子的,也不是线程安全的。

在该代码发生的是:

  • 许多(高达100)线程开始,各执行相同的代码
  • 在无限循环:
    • 非同步的方法nextSerialNumber是调用后返回运算符的结果调用静态变量
    • 调用了一个同步方法包含的内容,该函数检查返回的值是否存在于基础集合中
    • 如果是,该程序被终止
    • 如果不是,该值被添加到底层集合

如果增加后的操作是线程安全的,则程序将打印,也不要“重复“并且永不终止,因为每个线程都会得到不同的序列号值。情况并非如此,因为两个线程 可能获得完全相同的序列号值。