Day129.JUC:CAS 自旋锁、AQS、ReentrantLock底层实现、回顾Stream

目录

CAS 自旋锁 AtomicInteger

AQS Lock底层

ReentrantLock底层原理

彩蛋,回顾Stream流


CAS 自旋锁 AtomicInteger

CAS:Compare and Swap 比较并交换的。CAS操作有3个基本参数:内存地址A,旧值B,新值C。它的作用是将指定内存地址A的内容与所给的旧值B相比,如果相等,则将其内容替换为指令中提供的新值C;如果不等,则更新失败。类似于修改登陆密码的过程。

CAS是解决多线程并发安全问题的一种乐观锁算法,保证原子性因为它在对共享变量更新之前,会先比较当前值是否与更新前的值一致,如果一致则更新,如果不一致则循环执行(称为自旋锁),直到当前值与更新前的值一致为止,才执行更新。

JMM中更新内存变量,就使用了自旋锁

Day129.JUC:CAS 自旋锁、AQS、ReentrantLock底层实现、回顾Stream_第1张图片

Unsafe 类是CAS的核心类,提供硬件级别的原子操作(目前所有CPU基本都支持硬件级别的CAS操作)。

// 对象、对象的属性地址偏移量(内存地址)、预期值(旧值)、修改值(新值)
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public class CasDemo {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {

        //CAS : campareAndSwapInt(对象,偏移量,旧值,新值)
        //AtomicInteger 具有原子性操作的类
        AtomicInteger i = new AtomicInteger(1);

        System.out.println("第一次更新" + i.compareAndSet(1,200)); //200
        System.out.println("第一次更新后的值" + i.get()); //true
        System.out.println("第二次更新" + i.compareAndSet(1,300)); //200
        System.out.println("第二次更新后的值" + i.get()); //false
        System.out.println("第三次更新" + i.compareAndSet(200,300)); //300
        System.out.println("第三次更新后的值" + i.get()); //true

    }
}

验证原子性:

class DataOne{
    //private volatile Integer number = 0;
    private  AtomicInteger number = new AtomicInteger(0);
    //验证volatile不具备原子性,synchronized具备原子性
    public synchronized Integer incr(){
        //return ++number;
        return number.incrementAndGet();
    }
}

public class VolatileAtomicDemo {
    public static void main(String[] args) {
        DataOne dataOne = new DataOne();
        //1000个线程调用+1, 方法:正确结果应该是1000
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                System.out.println(dataOne.incr());
            }).start();
        }
    }
}

为什么JUC下atomic类 是原子性的

Day129.JUC:CAS 自旋锁、AQS、ReentrantLock底层实现、回顾Stream_第2张图片

缺点

开销大:在并发量比较高的情况下,如果反复尝试更新某个变量,却又一直更新不成功,会给CPU带来较大的压力

ABA问题:当变量从A修改为B再修改回A时,变量值等于期望值A,但是无法判断是否修改,CAS操作在ABA修改后依然成功。

不能保证代码块的原子性:CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。(要想整个代码块的原子性,加锁)

AQS Lock底层

AQS:AbstractQueuedSynchronizer 抽象队列同步器。它是实现同步器的基础组件(框架),JUC 中Lock的实现 (ReentrantLock、ReentrantReadWriteLock) 以及一些并发工具类 
(CountDownLatch、CyclicBarrier、Semaphore) 就是通过AQS来实现的。具体用法是通过继承AQS实现其模板方法,然后将子类作为同步组件的内部类

但是,StampLock不是基于AQS实现的 (Java8推出,ReentrantReadWriteLock 升级版,读多写少时可避免饥饿问题)。

AQS维护了一个volatile语义的共享资源变量state(支持多线程下的可见性),和一个FIFO线程等待队列(first-in-first-out)(多线程竞争state资源被阻塞时,会进入此队列)。

基于AQS实现锁的思路

AQS将大部分的同步逻辑均已经实现好,继承的自定义同步器只需要实现state的获取(acquire)和释放(release)的逻辑代码就可以,主要包括下面方法:

  • tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。

  • tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。

  • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

  • tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

  • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。

也就是说:

通过AQS可以实现独占锁 (一个线程可获取的锁,ReentrantLock 可重入锁),也可以实现共享锁 (多个线程可获取的锁Semaphore(信号量)/CountDownLatch(倒计时线程控制)等) 

ReentrantLock底层原理

以ReetrantLock为例, 说明AQS在锁底层的应用。

Lock锁是用AQS来实现的,资源抢占上锁用到了CAS。

Day129.JUC:CAS 自旋锁、AQS、ReentrantLock底层实现、回顾Stream_第3张图片

在ReentrantLock类中包含了3个AQS的实现类:
1. 抽象类Sync
2. 非公平锁实现类NonfaireSync
3. 公平锁实现类FairSync 

FairSync 底层实现 (AQS)

上锁:

Day129.JUC:CAS 自旋锁、AQS、ReentrantLock底层实现、回顾Stream_第4张图片

上锁具体步骤:

当等待队列只有一个线程时,直接获取到锁
如果队列不止一个线程,并且下一个线程就是当前申请锁的线程(可重入锁),则获取锁

Day129.JUC:CAS 自旋锁、AQS、ReentrantLock底层实现、回顾Stream_第5张图片

补充:各种锁,synchronized:偏向锁(偏向第一个线程,效率最高) ---> 如果有线程竞争升级为轻量级锁(自旋锁) ---> 自旋10次升级为重量级锁(悲观锁

彩蛋,回顾Stream流

1. 四大函数式接口

Day129.JUC:CAS 自旋锁、AQS、ReentrantLock底层实现、回顾Stream_第6张图片

2. Stream流

数据源 + 计算 + 结束操作

找出符合条件的数据
* 偶数ID
* 年龄大于24
* 用户名转大写
* 用户名字倒排序
* 只输出一个
* 用户名字
* */
public class StreamDemo {
    public static void main(String[] args) {
        User u1 = new User(11, "a", 23);
        User u2 = new User(12, "b", 24);
        User u3 = new User(13, "c", 22);
        User u4 = new User(14, "d", 28);
        User u5 = new User(16, "e", 26);

        List list = Arrays.asList(u1, u2, u3, u4 ,u5);
        list.stream().filter(p->{return p.getId() % 2 == 0;})
                .filter(p->{return p.getAge() > 24;})
                .map(f->{return f.getUserName().toUpperCase();})
                .sorted((o1,o2)->{return o2.compareTo(o1);})  //复制小括号 写死右箭头 落地大括号
                .limit(1).forEach(System.out::println);  //E

    }
}

关于面试,你还有什么要了解的吗

1. 公司技术部门的团队架构,前端、java、测试、运维团队人员。部门有的高手吗

2. 如果可以进入贵公司,对于试用期的员工,相关的技术、业务考核要求是什么 (底线)

3. 每年的新人一定很多,一定有很多优秀的,他的技术能力能达到什么水平 (上线)

你可能感兴趣的:(Java,java,开发语言)