【并发编程】锁

乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。在 Java中 java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁,就是在做操作之前先上锁。
摘要由CSDN通过智能技术生成

目录

1、锁的分类

1.1 可重入锁、不可重入锁

1.1.1 定义

1.2 乐观锁、悲观锁

1.2.1 定义

1.3 公平锁、非公平锁

1.3.1 定义

1.4 互斥锁、共享锁

1.4.1 定义

2、synchronized 

2.1 类锁,对象锁

2.2 synchronized 优化

2.3 synchronized实现原理

2.4 synchronized的锁升级

2.5 重量锁底层ObjectMonitor

ObjectMonitor核心属性:

C++中实现的加锁流程:

TryLock:

try_enter:

enter

EnterI

3、AQS概述

3.1 AQS重要参数

3.2 AQS常见的问题

3.2.1 AQS中为什么要有一个虚拟的head节点

3.2.2 AQS中为什么选择使用双向链表,而不是单向链表

4、ReentrantLock

4.1 ReentrantLock 与 synchronized 区别

5、RenntrantReadWriteLock

5.1 为什么要有读写锁


1、锁的分类

1.1 可重入锁、不可重入锁

1.1.1 定义

        重入:当前线程获取到A锁,在获取之后尝试再次获取A锁是可以直接拿到的。

        不可重入:当前线程获取到A锁,在获取之后尝试再次获取A锁,无法获取到的,因为A锁被当前线程占用着,需要等待自己释放锁再获取锁。

        Java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是可重入锁。

1.2 乐观锁、悲观锁

1.2.1 定义

        乐观锁:乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。

        在 Java中 java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。

        悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁,就是在做操作之前先上锁。

        Java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是悲观锁。

1.3 公平锁、非公平锁

1.3.1 定义

        公平锁:当有线程占用锁时,并有线程在排队时,直接排在线程后面,排序等待。

        非公平锁:线程一进来就会尝试获取一波锁资源,如果获取失败,再去排队等待。

        Java中提供的synchronized只能是非公平锁。

        Java中提供的ReentrantLock,ReentrantReadWriteLock可以实现公平锁和非公平锁

1.4 互斥锁、共享锁

1.4.1 定义

        互斥锁:同一时间点,只会有一个线程持有者当前互斥锁。

        Java中提供的synchronized、ReentrantLock是互斥锁。

        共享锁:同一时间点,当前共享锁可以被多个线程同时持有。

        Java中提供的ReentrantReadWriteLock,有互斥锁也有共享锁。(读锁是共享锁)

2、synchronized 

2.1 类锁,对象锁

        synchronized 是基于对象实现的,一般使用同步代码块或同步方法的方式。

        类锁:在static修饰的类或方法上使用synchronized 修饰时,使用当前类的class作为锁对象。

        对象锁:修饰非static 修饰的类或方法时,synchrozined会以当前实例对象作为锁对象。

           

public class MiTest {

    public static void main(String[] args) {
        // 锁的是,当前Test.class
        Test.a();

        Test test = new Test();
        // 锁的是new出来的test对象
        test.b();
    }

}

class Test{
    public static synchronized void a(){
        System.out.println("1111");
    }

    public synchronized void b(){
        System.out.println("2222");
    }
}

2.2 synchronized 优化

锁消除:在synchronized修饰的代码中,如果不存在操作临界资源的情况,会触发锁消除,你即便写了synchronized,他也不会触发。

public synchronized void method(){
    // 没有操作临界资源
    // 此时这个方法的synchronized你可以认为木有~~
}

锁膨胀:如果在一个循环中,频繁的获取和释放做资源,这样带来的消耗很大,锁膨胀就是将锁的范围扩大,避免频繁的竞争和获取锁资源带来不必要的消耗。

public void method(){
    for(int i = 0;i < 999999;i++){
        synchronized(对象){

        }
    }
    // 上面的代码会触发锁膨胀,变成下面这种形式
    synchronized(对象){
        for(int i = 0;i < 999999;i++){

        }
    }
}

锁升级:ReentrantLock的实现,是先基于乐观锁的CAS尝试获取锁资源,如果拿不到锁资源,才会挂起线程。synchronized在JDK1.6之前,完全就是获取不到锁,立即挂起当前线程,所以synchronized性能比较差。

  • 无锁、匿名偏向:当前对象没有作为锁存在。
  • 偏向锁:如果当前锁资源,只有一个线程在频繁的获取和释放,那么这个线程过来,只需要判断,当前指向的线程是否是当前线程 。
    • 如果是,直接拿着锁资源走。

    • 如果当前线程不是我,基于CAS的方式,尝试将偏向锁指向当前线程。如果获取不到,触发锁升级,升级为轻量级锁。(偏向锁状态出现了锁竞争的情况)

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lxtx-0510

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值