谈论锁就离不开CAS,CAS是compare and swap的缩写,从字面上理解就是比较并更新;主要是通过 处理器的指令 来保证操作的原子性
简单来说:程序先要得到一个值A,这个值正常就是从我们要更新的内存位置得到的,进行CAS操作的时候,我们需要传入两个值,一个是A,一个是我们要修改的值B,此时才正式进入原子操作
从内存位置V上取到存储的值,将值和预期值A进行比较,如果值和预期值A的结果相等,那么我们就把新值B更新到内存位置V上,如果不相等,那么就重复上述操作直到成功为止。
CAS是一种无锁化编程,是一种非阻塞的轻量级的乐观锁;而synchronized是阻塞式的重量级悲观锁
CAS实现
下面是AtomicInteger类里一个更新值的方法
public final int updateAndGet(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return next;
}
这里的get()是获取要操作的值,next就是要更新的值,compareAndSet就是上面说的CAS操作,底层是native方法,他可以保证操作的原子性,当操作不符合预期(即A与V不相等,或者说prev与操作时候读出来的V不相等),就会返回false,然后这个操作就会一致循环,直到成功,他的value也是用volatile修饰的,保证修改可见(不然有可能get出来的值一直是旧值,这样就变成死循环了)
CAS+Volatile= 同步代码块
通过CAS+Volatile关键字,我们可以实现一个乐观锁,来完成同步操作
Volatile用于修饰一个标志位,可以理解为标志一个锁是否被占有,通过Volatile关键字我们可以保证所有线程都可以访问到这个标志位最新的值,CAS主要用于完成原子操作
实现步骤
- 使用 volatile 关键字修饰一个int类型的同步标志位state,初始值为0;
- 加锁/释放锁时使用CAS操作对同步标志位state进行更新; 加锁成功,同步标志位值为 1,加锁状态; 释放锁成功,同步标志位值为0,初始状态;
参考加锁代码
/**
* 加锁,非公平方式获取锁
*/
public final void lock() {
while (true) {
// CAS操作更新同步标志位
if (compareAndSetState(0, 1)) {
// 将独占锁的拥有者设置为当前线程
exclusiveOwnerThread = Thread.currentThread();
System.out.println(Thread.currentThread() + " lock success ! set lock owner is current thread . " +
"state:" + state);
try {
// 睡眠一小会,模拟更加好的效果
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 跳出循环
break;
} else {
// TODO 如果同步标志位是1,并且锁的拥有者是当前线程的话,则可以设置重入
if (1 == state && Thread.currentThread() == exclusiveOwnerThread) {
// 进行设置重入锁
}
System.out.println(Thread.currentThread() + " lock fail ! If the owner of the lock is the current thread," +
" the reentrant lock needs to be set;else Adds the current thread to the blocking queue .");
// 将线程阻塞,并将其放入阻塞列表
parkThreadList.add(Thread.currentThread());
LockSupport.park(this);
// 线程被唤醒后会执行此处,并且继续执行此 while 循环
System.out.println(Thread.currentThread() + " The currently blocking thread is awakened !");
}
}
}