本文主要是介绍CAS 和 Sychronized的在CPU密集计算情况下的博弈,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
很多时候听到使用CAS很优秀。。。
但是CAS是银弹吗?
从实现角度上,sychronized当线程获得不到锁的时候把线程挂起,而CAS不会挂起,而是继续重试。
比如下面的一个场景
i++ 的场景,以下代码是AtomicInteger
的i++源码
public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);}
底层是用的就是unsafe
的CAS
实现。
public static void main(String[] args) throws InterruptedException {AtomicInteger i = new AtomicInteger();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {long start = System.currentTimeMillis();for (int j = 0; j < 100000000; j++) {i.incrementAndGet();}long end = System.currentTimeMillis();System.out.println(end - start + " ms");}});t1.start();t1.join();}
测试1亿次计算(粗略计算),458ms,cpu差不过50%左右。(i7-8700)
那么也就是说1ms可以计算2000000(2百万次)
因为我们计算一次的时候,需要调用cas函数一次,
那么两个线程竞争的时候,可以得出线程1(成功了)调用了CAS1次,而线程2调用了CAS两次
其实,我们可以得出一个公式
f(1) = 1,
f(2) = 2- -加上重试失败的一次,
f(2) =3 --加上重试失败的两次,
…
f(n) = n
那么也就是说如果有200个线程(201核cpu)一致在重试的话,
200! = f(200) + f(199) +…+f(2)+f(1) = 20100次
相当于200个线程同时竞争的情况下,i++成功计算的是0.02ms。
这点时间是微不足道
但是再回想一下区别,synchronized会阻塞线程,或者说是挂起
因为java程序在挂起唤醒线程的时候需要从用户态切换到内核态,这是非常消耗资源的。
那么这个时间消耗是多少呢?
不得而知,相比于0.02ms应该还是大些,或者小一些。
以上的例子举的是运算极快(释放锁极快,成功率高)的场景
但是当存在成功率低的,或者是上面的例子丰富一下,造一个下面的
这个程序其实应该用long的
我把主线程睡了120s(),还没有算完。但是如果按照我们之前的一个线程一个线程的计算的话,500ms * 200 = 100s也能算完,并且CPU也不会满载。
所以这个时候使用sychronized是不是也很合适呢?
这篇文章主要是表示了我对线程阻塞和不阻塞对时间损耗的一些思考。场景可能有些模糊。欢迎拍砖!!!
注:当然synchronized现在也有自旋锁。
这篇关于CAS 和 Sychronized的在CPU密集计算情况下的博弈的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!