# count++与count+=1区别
没什么区别, 都不具备原子性.
# 多线程跑count++会有什么问题
在n个线程跑count+=1, 众所周知, 结果肯定和单线程运算n次不一样, 会比单线程运算n的结果要少.
这其中主要包括两个原因: 可见性和原子性.
可见性: 由于多核CPU缓存原因, 导致存在缓存与内存中数据不一致的问题, count先从内存中读到了各自CPU的缓存中, 运算后, 同样写入到各自缓存中, 而此时CPU缓存与内存同步不是实时的, 因此才会出现不一致的问题. 对此在Java中可以使用
volatile修饰field, 确保其可见性, 强制从内存中读取和写入数据, 但同时性能也会下降几个数量级. 具体实现应该是利用的处理器提供的两个内存屏障指令(memory barrier).原子性: count++或者count+=1都是不具备原子性的, 因为它们在系统层面不属于一个最小的操作单元. 其包括3个步骤: 1.将内存中count值读取出来 2.操作加+1 3.将结果写入内存 按照上面步骤, 多线程并行时, 会出现一个线程写入内存覆盖另一个线程之前写入的值. 要解决此问题, 必须将count++或者count+=1变成同步操作, 可以使用
synchronized同步块或者atomic变量, 也可以在线程中各自计算结果, 最后相加.
// 示例代码
public class ThreadTest {
private long count = 0;
private static final long max = 1000000000;
private void add10K() {
int idx = 0;
while(idx++ < max) {
count += 1;
}
}
public static long calc() throws InterruptedException {
final ThreadTest threadTest = new ThreadTest();
Thread th1 = new Thread(threadTest::add10K);
Thread th2 = new Thread(threadTest::add10K);
th1.start();
th2.start();
th1.join();
th2.join();
return threadTest.count;
}
public static void main(String[] args) throws InterruptedException{
long count = ThreadTest.calc();
System.out.println(count);
}
}
# FAQ
# 循环10亿次时, 为什么结果值只有898741597?
count用volatile修饰时, 会出现如题的结果, 如果保证count的线程安全, 结果应该是20亿, 而现在的结果都没有达到
单线程10亿次. 使用volatile解决了可见性问题, 但由于原子性问题, 仍然无法达到正确的数值, 而此时结果没有达到单线程10亿的值, 完全是因为运气问题- -! 要出现count<单线程跑的值, 肯定是由于在某一个极小时间范围中, 出现走得快线程写入内存中的值被走得慢线程计算的值所覆盖, 导致走得快线程拿到了一个小于上次计算的值, 这种情况在使用了volatile后, 由于线程读写都是直接操作内存, 所以会频繁出现, 因此容易导致最终结果达不到单线程10亿次的结果.不使用volatile的情况下, 基本没出现过<
单线程10亿次的结果, 这主要是因为在所有过程中, 基本没有缓存与主内存进行数据同步(这一块的知识点很多, 需要了解JMM和cpu缓存机制, 包括失效队列等), 也就是基本都是操作各自缓存中的数据, 而结果不是单线程10亿次, 则是因为两个线程不是同时启动的.
# 为什么循环1万次结果是接近2万?
main线程中start()两个线程启动有延迟, 或者说多个线程并不是同时启动的.