ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

JAVA锁

2020-01-11 13:56:26  阅读:293  来源: 互联网

标签:那么 JAVA 对象 东西 主存 state 线程


前言

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

标签:那么,JAVA,对象,东西,主存,state,线程
来源: https://www.cnblogs.com/huangtao1927/p/JAVA.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有