JAVA锁

前言

JAVA中常用的锁其实是有很多的,但是,一般来说我们常见到的可能就是几种

下面用一个图来简单表示一下

下面来简单介绍一下

一,synchronized

synchronized关键字的用法有很多,最常用的可能就是在一个方法上加上这个关键字,然后就锁住了,那么它的原理是什么呢?

我们知道JAVA对象其实分为对象头,对象体,对齐填充。

先解释一下这三个东西

1,对象头

这里面有什么东西呢?都是一些隐性的必须品

你一个对象要放在内存,你要有地址吧?那么有个东西叫内存地址,也就是哈希码

你一个对象是哪个线程的,你要知道吧?那么有个东西是threadId。

你一个对象有锁吧?那么有个东西是锁

你一个锁有状态吧?那么有个东西是锁状态

你一个对象要GC吧?那么又个东西叫GC标志

等等。

2,对象体

你一个对象有数据吧?都在里面

3,对齐填充

这玩意儿其实就是对齐用的,相信大家学JAVA的时候都学过基本数据类型int,long什么的,他们占多少字节,对吧?这东西,大家学过,但是很容易就忘记了,因为敲代码没什么用。那么一个对象占多少字节呢?

一般来说是8的倍数,那么对齐填充就是来补位的。也就是对象头+对象体+对齐填充=n*8。

 

在对象头里有个东西叫Monitor,synchronized能锁住,其实就是靠这个东西。

那么加了锁,就要释放,那么释放又是什么呢?是由JVM来做的。

synchronized的底层其实是什么呢,其实是Mutex,这个东西是什么呢?大家都知道JVM是C和汇编弄出来的。Mutex其实是这个层面的东西,比如对应在汇编里有个东西叫tsl(大家百度一下)。

那么一个synchronized的执行过程大概是什么呢?

1. 检测Mark Word里面是不是当前线程的ID,如果是,表示当前线程处于偏向锁

2. 如果不是,则使用CAS将当前线程的ID替换Mard Word,如果成功则表示当前线程获得偏向锁,置偏向标志位1

3. 如果失败,则说明发生竞争,撤销偏向锁,进而升级为轻量级锁。

4. 当前线程使用CAS将对象头的Mark Word替换为锁记录指针,如果成功,当前线程获得锁

5. 如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

6. 如果自旋成功则依然处于轻量级状态。

7. 如果自旋失败,则升级为重量级锁。

 

二,AQS

这玩意儿叫同步器,一般来说,你写代码的时候,没碰过这东西。因为这是个父类,但是你写多线程的时候肯定用过它的实现类,比如ReentrantLock;Countdownlatch等。这两个东西怎么用就不说了。

他们的底层实现都是AQS。

复杂的源码分析就不说了。

简单点说就是下面这个图

 

AQS就是个链表构成的队列加上volatile关键字。

在AQS中有个属性

private volatile int state;

举个例子

单线程的时候

假设state=0,你用ReentrantLock的时候,锁住了一个方法,当一个线程来的时候进入队列,state变成了1,OK,方法往下走,走完了state=0,没问题。

多线程的时候:

假设state=0,你用ReentrantLock的时候,锁住了一个方法,当N个线程进来的时候进入队列,队列里第一个线程拿到了锁,state变成了1,OK,方法往下走;队列里第二个线程去拿锁,发现state=1,失败了,就等待,后面的可以简单认为也是这样。当第一个线程走完了,释放了锁,出队列了;第二个线程变成第一个了,拿到了锁,那么第二个就执行,以此类推。

 

三,CAS

这个玩意儿,一般来说,写代码的时候,你应该是没有用过的。如果你用IDEA,用了lambda表达式的流处理并且想改变里面的int一类类型的值的话,IDEA会提示你用AtomicInteger或数组。

AtomicInteger(原子类)这一类的东西底层就是CAS,中文就是比较和交换。他的底层是啥呢?

 

volatile,要了解这个东西,其实要知道JVM的线程模型,如下图

 

JVM的线程模型和操作系统是对应的,当一个线程过来的时候是需要到主存里去拿东西的。

举个例子

单线程的时候:

在主存里有X,线程A要修改X,那么来拿X,拿到之后,把X记录在线程A里,然后把X修改成Y,完了以后,就有2个东西了,一个X(旧),一个是X(新),现在要把X(新)放回主存,那么就要先拿X(旧)和主存里的X比较一下,看看是不是一样的,如果是就把主存里的X变成X(新),如果不是一样的,就失败了。

多线程的时候:

在主存里有X,线程A要修改X,线程B也要修改X,那么都来拿X,拿到之后,A把X记录在线程A里,B把X记录在线程B里,然后A把X修改成Y,B把X修改成Z,完了以后,就都有2个东西了,一个X(旧),一个是X(新),现在要把X(新)放回主存,那么就要先拿X(旧)和主存里的X比较一下,看看是不是一样的,如果是就把主存里的X变成X(新),如果不是一样的,就失败了,那么A和B肯定会有一个成功一个失败。

 

怎么样?是不是有点熟悉的感觉。CAS这个东西简单点说就是乐观锁 。

这里提一下,在内存里有个东西叫缓存一致性协议,这个东西其实是volatile能实现的原因。

 

下面还有一些经常听见,但是其实你并没有接触的一些锁

1,自旋锁

这玩意其实是个优化功能,你写代码是不会写到它的,都是源码实现的。就是线程的唤醒,休眠什么的耗性能,所以弄了个这个东西出来,他是怎么实现的呢?其实就是for循环。。。

2,偏向锁

这玩意其实也是个优化功能,你写代码是不会写到它的,都是源码实现的。就是一个锁,第一个线程进来的时候,会记录一下这个线程,如果下一个也是他的话,就会比较快。。。

3,可重入锁

这玩意,你写代码是不会写到它的,都是源码实现的。其实就是一个线程拿到锁之后,他自己还可以拿第二次,第三次。。。代码的原理其实是有一个status的值记录一下,拿一次就+1,用完了就-1,再底层一点就是有个东西叫程序计数器。。。

4,乐观锁

CAS

5,悲观锁

synchronized

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值