synchronized
synchronized 是具有原子性的内置锁,
线程的执行代码进入 synchronized 代码块前会自动获取内部锁,其他线程访问该代码块就会被阻塞,
正常退出、抛出异常、或调用了 wait 方法时释放锁。
synchronized 阻塞一个线程时,需要从用户态切换到内核态执行阻塞操作,是耗时操作。
synchronized 块内使用到的变量会从工作内存清除,使用到这些变量时就不会从工作内存获取,而是直接从主内存读取。退出同步代码块这些变量会重新刷新到主内存。这样就解决了内存可见性问题。
valatile
valatile 确保对变量的更新对其他线程马上可见。
实现原理:修改该变量时,如果该变量在其他 CPU 中存在副本,会通知其他 CPU 将该变量缓存设置为无效状态。
valatile 解决了可见性,不保证原子性。
写 valatile 变量时,可以确保 valatile 写之前的操作不会被编译器重排到 写之后;
读 valatile 变量时,可以确保 valatile 读之后的操作不会被编译器重排到 读之前。
指令重排:不存在数据依赖性的指令可能会指令重排。
指令重排可能在多线程下会导致非预期结果,单线程没有这种问题。
CAS
CAS 即 JDK 里 Unsafe 类提供的一系列 比较交换 compareAndSwap* 方法,这些方法都是 native 方法,是操作系统级别的原子性方法。
缺点:
- 只能保证一个变量的原子性操作。多变量原子性操作可以用锁。
- ABA 问题:获取值为 A,计算出 B,CAS 修改 A 为 B。期间 A 可能已经经过了 A 到 B,B 到 A 的过程。这种情况 CAS 无法察觉。
- 所以 JUC 包提供了 AtomicStampReference 类给每个变量又加了时间戳,来避免 ABA 问题。