CAS和synchronized原理

Synchronized 原理

  1. synchronized 既可以是乐观锁, 也可以是悲观锁.
  2. synchronized 既可以是轻量级锁, 也可以是重量级锁.
  3. synchronized 重量级锁是由系统的互斥锁实现的; 轻量级锁是基于自旋锁实现的.
  4. synchronized 是非公平锁(不会遵守先来后到).
  5. synchronized 是可重入锁(内部会记录哪个线程拿到了锁, 记录引用计数).
  6. synchronized 不是读写锁.

Synchronized的内部实现策略?
可能会产生一系列的"自适应"的过程, 叫做锁升级.
无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁.

加锁工作过程

  • 偏向锁: 偏向锁不会真的加锁, 只是做了一个标记.如果有别的线程来竞争了, 才会真的加锁.如果没有别的线程竞争, 就始终不会真的加锁.(能不加就不加)
  • 轻量级锁: synchronized通过自旋锁的方式实现轻量级锁.
    一个线程把锁占据之后, 另一个线程就会按照自旋的方式反复查询当前锁是否被释放了,
    但是, 如果这把锁的线程越来越多了(锁竞争更激烈了), 就会升级为重量级锁.

一些优化

  • 锁消除: 编译器会智能的判定当前代码是否要加锁, 如果你写了加锁, 实际上没有必要加锁, 就会把加锁操作自动删除掉.
  • 锁粗化: 锁的粒度?
    我们一般认为, 如果加锁操作中包含的实际要执行的代码越多, 就认为锁的粒度越大.
    锁的粒度越小, 并发程度就更高.
for(...) {
	synchronized (this) {
		count++;
	}
}

锁的粒度越大, 效率就越高.

synchronized (this) {
	for(...) {
		count++;
	}		
}

CAS

CAS(Compare and swap).
假设内存中的原数据V, 旧的预期值A, 需要修改的新值B.
1. 比较AV是否相等.
2. 如果比较相等,B写入V.
3. 返回操作是否成功.

使用CAS伪代码来辅助理解CAS的工作流程, 不是原子性代码.

// address: 内存地址. expectValue: 寄存器中的值. swapValue: 相等就替换的值
boolean CAS(address, expectValue, swapValue) {
	if(&address == expectValue) {
		&address = swapValue;
		return true;
	}
	return false;
}

但是这段代码逻辑, 是通过一条cpu指令完成的, 具备原子性, 就给我们编写线程安全代码, 打开了新大门.

实现原子类

标准库中提供了一组原子类, 最典型的是AtomicInteger类.
在这里插入图片描述

在这里插入图片描述

有两个构造方法.
// 设置初值为0
AtomicInteger();
// 设置初值为initialValue
AtomicInteger(int initialValue);

提供了一系列方法:

// 前置++
public final int getAndIncrement() {
	Atomically increments by one the current value.
	Returns:
	the previous value
}
// 前置--
public final int getAndDecrement() {
	Atomically decrements by one the current value.
	Returns:
	the previous value
}
// 后置++
public final int incrementAndGet() {
	Atomically increments by one the current value.
	Returns:
	the updated value
}
// 后置--
public final int decrementAndGet() {
	Atomically decrements by one the current value.
	Returns:
	the updated value
}
public class Demo28 {
    // 设置初值为0
    private static AtomicInteger count = new AtomicInteger(0);
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                count.getAndIncrement();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                count.getAndIncrement();
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();
        System.out.println(count);
    }
}
// 结果是10000, 是原子性的.

下面使用伪代码实现原子类:

class AtomicInteger {
    private int value;
    // 前置++
    public int getAndIncrement() {
        int oldValue = value;
        while ( CAS(value, oldValue, oldValue+1) != true) {
            oldValue = value;
       }
        return oldValue;
   }
}
// oldValue, value, oldValue+1 都是寄存器中的值
// 如果value和oldValue相等, 就return oldValue
// 如果不等, 就把oldValue = value;

为啥会出现oldValue != value的情况呢??
因为很可能出现有别的线程穿插在两段代码之间, 把它修改了.
在这里插入图片描述
以上的图很好的描绘了如何修改代码的…

当两个线程并发的执行++操作时,如果不加任何限制,其一,就会出现串行化;
其二, 就会出现穿插, 会使结果出现问题.
通过加锁保证线程安全,强制避免穿插.
而原子类CAS保证线程安全,借助CAS来识别当前是否出现了穿插情况,如果没穿插,此时直接修改.
如果穿插了,就会重新回去内存中的值,再次尝试修改. 
多个CAS线程访问内存时,一定会有先后顺序的.
因为多个cpu在操作同一个资源, 也会涉及到锁竞争(指令级别的锁).synchronized实现的
锁要轻量许多(cpu内部实现的机制).

小结

本文讲述了synchronized的原理, 以及CAS实现原子类.
博主会在下篇博客中更新CAS实现自旋锁, 以及ABA问题和相关面试题.
希望有收获的小伙伴多多支持!

相关推荐

  1. synchronized底层加锁释放锁的原理

    2024-04-23 08:24:03       33 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-23 08:24:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-23 08:24:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-23 08:24:03       82 阅读
  4. Python语言-面向对象

    2024-04-23 08:24:03       91 阅读

热门阅读

  1. 【重学C语言】九、函数

    2024-04-23 08:24:03       38 阅读
  2. internet.getUserEncryptKey提示错误

    2024-04-23 08:24:03       29 阅读
  3. 从零开始:UniApp 项目搭建指南

    2024-04-23 08:24:03       39 阅读
  4. uniapp picker组件实现二级联动

    2024-04-23 08:24:03       31 阅读
  5. Linux搭建NFS服务器

    2024-04-23 08:24:03       36 阅读
  6. 特殊类的设计、C++四种类型转换

    2024-04-23 08:24:03       42 阅读
  7. 2024系统架构师---论软件系统架构评估

    2024-04-23 08:24:03       37 阅读
  8. 狠不狠?做个标签累不累?

    2024-04-23 08:24:03       33 阅读
  9. wow-socket文件说明

    2024-04-23 08:24:03       29 阅读
  10. 3D抓取算法中的PointNet++网络介绍

    2024-04-23 08:24:03       29 阅读