Synchronized和CAS

什么是CAS?

CAS的全称是 Compare And Swap(Compare And Exchange) 比较并交换,

cas(v,a,b) 变量v,期待值a, 修改值b

Synchronized和CAS_第1张图片

CAS底层通过Lock指令实现。

以 java.util.concurrent.atomic包下的 AtomicInteger 为例

Synchronized和CAS_第2张图片

调用的unsafe.getAndAddInt

Synchronized和CAS_第3张图片

getAndAddInt 是一个do..while死循环调用compareAndSwapInt方法

compareAndSwapInt 是一个native方法

jdk8u: unsafe.cpp:

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  UnsafeWrapper("Unsafe_CompareAndSwapInt");
  oop p = JNIHandles::resolve(obj);
  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END

jdk8u: atomic_linux_x86.inline.hpp 

inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
  int mp = os::is_MP();
  __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)"
                    : "=a" (exchange_value)
                    : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
                    : "cc", "memory");
  return exchange_value;
}

最终是通过LOCK_IF_MP 实现。

CAS的ABA问题

什么是aba问题,打个比方, 要修改的变量a的值原本是1,被另一个线程改成了2之后又改成了1,这个时候1还是1,但它已经被修改过了。
举个例子:你的女朋友在和你分开后经历了别的男人又回到你身边怎么办?当然是选择原谅她

解决方法,加版本号,AtomicStampedReference类

CAS是 乐观锁 / 自旋锁 / 轻量级锁 ,之前的synchronized是重量级锁,现在有锁升级过程。

Synchronized

synchronized锁的是对象而不是代码,通过对象头上的两位控制是不是加了锁,加了什么类型的锁。

64位hotspot的实现为

Synchronized和CAS_第4张图片

001 表示 无锁态
101 表示偏向锁
00   表示轻量级锁
10   表示重量级锁
11   GC回收标志

锁升级过程

1、对象刚创建时是 无锁 或者 匿名偏向锁
匿名偏向:如果偏向锁打开了就是匿名偏向锁,没打开就是无锁,匿名偏向锁在JVM启动4秒后打开,延迟4秒是因为JVM启动的时候就已经知道哪些对象有竞争,就直接设置为轻量级锁,如果设置成匿名偏向锁还要撤销锁升级成轻量级锁,消耗资源。

2、如果有线程上锁,上偏向锁,偏向锁就是把markword的线程ID改为自己线程ID,下次同一个线程加锁的时候,不需要争用,只需要判断下线程指针是否是同一个,所以,偏向锁,偏向加锁的第一个线程。hashcode备份在线程栈上,线程销毁,锁降级成无锁

3、如果有线程竞争撤销偏向锁升级为轻量级锁自旋锁),每个线程都有自己的LockRecord在自己的线程栈上,用CAS去争用加锁对象markword的LR的指针,指针指向哪个线程的LR,哪个线程就拥有锁。

4、如果竞争加剧,升级重量级锁,向操作系统申请资源,线程挂起,进入等待队列,等待操作系统的调度。
竞争加剧:JDK1.6之前自旋10次或者自旋线程数超过CPU核心数一般升级重量级锁,JDK1.6加入自适应自旋,由JVM自己控制。

CAS和Synchronized的对比

CAS底层通过LOCK_IF_MP指令实现,在用户态执行,不用经过操作系统老大,效率高。但死循环消耗CPU,所以CAS适合线程低竞争,低耗时的场景。
synchronized升级为重量级锁后线程交给操作系统老大,效率低,但是加入等待队列后不消耗CPU,所以适合高竞争,高耗时的场景。

你可能感兴趣的:(JDK源码,java)