CAS compareAndSwap 比较和交换
原理:比如boolean flag = false;然后多线程去修改flag = true;确保同时只有一个线程(比如A)能够修改成功,一旦修改flag = true;,其他线程判断为true后只能一直等待。当线程A执行完毕,修改flag = false;,其他等待中的线程(包括A)同时又可以竞争去修改flag = true。
以此标记只有同一时间只有一个线程能够执行。
其中有原子数操作可以保证这种修改的线程安全:
atomicInteger.compareAndSet(UNLOCK, LOCK);
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
说明下,unsafe.compareAndSwapInt(this, valueOffset, expect, update)
,unsafe类是脱离java环境的直接操作内存地址(存在内存泄露安全问题,谨慎操作),valueOffset是unsafe实例化的时候设置的一个变量的内存地址引用,
compareAndSwapInt
做的是:
- 比较valueOffset地址上变量的值是否和参数expect相等,不相等则返回false
- 相等,则修改这个值变为参数update
由于是实际内存地址操作,所以具有原子性,上面两部不存在线程安全问题。
简单的CAS锁代码:
import java.util.concurrent.atomic.AtomicInteger;
public class CASLock {
public static final int UNLOCK = 0;
public static final int LOCK = 1;
AtomicInteger atomicInteger = new AtomicInteger(UNLOCK);
//加锁
public void lock() {
do {
//等待其他线程释放锁(修改值为UNLOCK )
} while (!atomicInteger.compareAndSet(UNLOCK, LOCK));
}
//释放锁
public void unlock() {
if (!atomicInteger.compareAndSet(LOCK, UNLOCK)) {
throw new RuntimeException("你还没获得锁!");
}
}
}
测试:
import java.util.concurrent.CountDownLatch;
public class Test4 {
static CountDownLatch countDownLatch = new CountDownLatch(100);
final static CASLock casLock = new CASLock();
static int i = 0;
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
// casLock.lock();
for (int j = 0; j < 100; j++) {
i++;
// System.out.print(Thread.currentThread().getId()+" ");
}
countDownLatch.countDown();
// casLock.unlock();
}
};
ThreadPool threadPool = new ThreadPool(100);
for (int j = 0; j < 100; j++) {
threadPool.addTask(runnable);
}
countDownLatch.await();
System.out.println(i);
threadPool.close();
}
}
其中ThreadPool 类:https://blog.csdn.net/liangwenrong/article/details/106581838
加锁和不加锁的区别明显,加CAS锁后线程互不干扰,结果都是10000,
否则不是,因为i++操作不是线程安全的,i++实际上是三步完成,有可能在过程中被其他线程修改。