场景:
创建20个线程,每一个线程循环给成员变量加一,循环次数为100次,理论结果线程结束后结果为2000.
下面先给出基本代码:
package com.springcloud.server.springserver.thread;
public class CasDemo {
private static int count = 0;
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
//每个线程让count自增1000次
for (int i = 0; i < 1000; i++) {
increase();
}
}
}).start();
}
try{
Thread.sleep(200);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(count);
}
static void increase(){
count++;
}
}
输出结果并不是2000,而是比2000小,这是因为count++这个操作,并非原子操作.
有两种方法可以解决这个问题:
- 使用悲观锁,直接在increase()方法上加上synchronized关键字,使得每一次只有一个线程进入这个自增的方法,不过这个办法不是最优的方法.
- 使用乐观锁,采用CAS的方法
下面给出使用CAS原理改造的代码:
package com.springcloud.server.springserver.thread;
public class CasDemo {
private static volatile int count = 0;
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
//每个线程让count自增1000次
for (int i = 0; i < 1000; i++) {
increase();
}
}
}).start();
}
try{
Thread.sleep(200);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(count);
}
static void increase(){
int expect;
while (!compareAndSwap(expect = getCount(),expect+1)){
}
}
public static synchronized boolean compareAndSwap(int expected ,int newCount){
if (getCount() == expected){
count = newCount;
return true;
}
return false;
}
public static int getCount(){
return count;
}
}
我们在return fasle打断点,查看这个cas的猫腻:
因为count使用了volatile修饰,所以只要你取这个count值,它(从主存取)一定是正确的,那么为什么这个值是19,而不是2呢?
其实上35行的代码有三个操作:
1.获取count值
2.count值加一
3.进入compareAndSwap()方法,进行判断(加锁)
我们的锁只能保证第三步的操作是原子性的,1,2,3这三个步骤并非原子性,假设某一个线程已经走了第一步和第二步,而且expected为2,newCount为3,但是现在count的值已经被其他线程改成了19,那么它就会进入false这个分支.然后它就会再次进入while循环,重新获取count的值,直到返回true,跳出循环,得到正确的值.
这样,简易版的CAS代码就完成了.