最近在看《实战 Java 高并发程序设计》,遇到一些问题,所以简单记录一下。上面两个参考资料都非常不错,由于时间有限,并没有全部看完,有些地方也不能完全看懂。第二个参考资料偏 C++,如果要看 Java 相关的书,或许可以看看《The Art of Multiprocessor Programming》。
So blocking is at least as fast as lockfree, while it can be faster (when it happened so that the fastest known algorithm is at least partially blocking).
publicstaticclassAddThreadimplementsRunnable { publicvoidrun() { synchronized (AtomicIntegerDemo.class) { for (intk=0; k < 10000000; k++) { i++; } } } }
publicstaticvoidmain(String[] args)throws InterruptedException { Thread[] ts = newThread[10]; for (intk=0; k < 10; k++) { ts[k] = newThread(newAddThread()); } longs= System.nanoTime(); for (intk=0; k < 10; k++) { ts[k].start(); } for (intk=0; k < 10; k++) { ts[k].join(); } longe= System.nanoTime(); System.out.println((e - s) / 1000000.); } }
我们使用 start /affinity 1 java geym.conc.ch4.atomic.AtomicIntegerDemo 命令设置程序的 CPU 亲和性,测试 AtomicInteger 代码在单核上的耗时,得到大约 500 ms 的输出,毕竟使用的是同一个 CPU 缓存,性能更高在意料之中。那么,如果设置使用锁的代码亲和单个 CPU,得到的结果依然是十几毫秒,毕竟加锁就是严格串行的,无法利用多核 CPU 的资源,即使在单核上执行也不会影响多少性能。
It is possible for processors to do that in general, but x86 doesn’t, so you don’t need a barrier there. Search for “total store order” if you’re curious.
大多数指令集架构不提供顺序一致的内存模型,因为更强的一致性通常意味着更少的优化(更低的性能)。x86 使用 Total Store Order(TSO)内存模型:所有处理器都连接到单个共享内存,但是每个处理器有一个本地的写入队列,写入操作排队写入共享内存,读取操作会优先读取本地写入队列中的值(如果有的话)。ARM/POWER 的内存模型更加宽松:每个处理器从自己的内存完整副本中读取和写入,读取可以延迟到写入之后,并且每个写入都独立地传播到其他处理器,在写入传播时允许重新排序。
JMM 为程序中所有操作定义了一个偏序关系,称为 Happens-Before。操作 A Happens-Before 操作 B 的含义是:如果操作 A 先于操作 B 发生,那么执行操作 B 的线程能够看到操作 A 的结果。如果两个操作没有 Happens-Before 关系,那么 JVM/CPU 可以对它们任意地重新排序。