先来看一段代码
private static AtomicInteger atomicInteger = new AtomicInteger(0);
private static int m = 0;
public static void main(String[] args) throws Exception{
Thread [] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for(int i = 0; i < threads.length; i ++){
threads[i] = new Thread(()->{
for (int j = 0; j < 100; j++) {
atomicInteger.incrementAndGet();
m ++;
}
latch.countDown();
});
}
Arrays.stream(threads).forEach((t)->{
t.start();
});
latch.await();
System.out.println("----------atomic int--------" + atomicInteger);
System.out.println("----------for int--------" + m);
}
现在有100个线程,每个线程循环100次,进行+1操作,同时操作atomicInteger 和 m这2个静态变量
从结果可以看出,使用JUC 下面的atomic能够得到目标值,而变量m 的结果并不是1W,原因肯定是多线程执行没有上锁,导致某一时刻有N个线程同时+1
那么为什么atomic没上锁,最后的结果却是1W呢?
什么是CAS
CAS(Compare And Swap)是指比较并交换。CAS算法 CAS(V,E,N)包含有3参数,V 表示要更新的变量,E 表示预期的值,N 表示新值。
这里画了个图,通过这个图很直观的就能知道CAS的处理逻辑,这里面实际上并没有任何的加锁操作,每次操作之前都会先进行比较,然后在替换,如果不相等,就会一直循环的执行下去,直到最终执行成功,这样是他为什么会被称为自旋锁;当然CAS也是一种乐观锁,总是认为自己是可以成功完成操作的。在有多个线程可以同时使用 CAS 操作一个变量时,只有一个会胜出并成功更新,其余均会失败。
CAS的缺点
- 自旋的时候会一直循环,会导致CPU高负荷
- 只能保证变量的原子性,并不能像Synchronized那样添加到方法上或者代码块上
- ABA问题
什么是ABA问题?
接着上面所说,当线程A在拿到m=0,进行+1操作的时候,此时有个线程B 进来他把m修改成1,然后又修改成0,此时线程A在进行比较的时候,虽然m这个值还是0,但是是被人修改过后的0,不是之前的那个0了,这就是ABA问题,A–>B–>A,如何解决?
- 添加时间戳
- 添加版本号
- 使用悲观锁Synchronized