Java中的锁详解

一 丶锁的作用

锁在根本上就是为了解决 线程安全问题; 线程安全问题可以从三个角度进行分析: 原子性 可见性 有序性;
思考:再多线程的环境中存在一个数据 int i=0,此时同多个线程(假设是两个线程)对它进行 i++操作结果是多少呢? 结果是i<=2
分析:i++这个操作不是原子性的;i++在Java中是一个操作,但是当它在底层汇编执行时被拆分成了三个操作:

1.从内存中加载 i 的值(get)
2.对i进行操作 (modify)
3.将i的值写入到内存中(set)

结果:i<=2 的原因是:多个线程可能是并发进行操作的所以可能同时读取到 i=0 然后进行操作 最终 i=1 或者多个线程没有进行并发操作 i=2;
问题出现的原因:i++的操作不是原子性;

二丶Java中锁的分类

从功能层面上分析Java中的锁分为两大类: 共享锁 排它锁
共享锁(读锁): 在同一时刻允许多个线程访问共享资源
排它锁(写锁):在同一时刻只允许单一个线程访问共享资源
在多线程环境下 并行是比串行的效率高的,加锁会导致线程阻塞问题.在程序中为了保证性能问题和线程并发的安全性
锁的优化方向:
1.锁粒度的优化 : 锁的范围缩小
2.无锁编程(乐观锁): CAS(原子性,可见性) 版本号机制
3.偏向锁,轻量级锁,重量级锁
4.锁消除/锁膨胀(编译器优化)
5.读写锁:读多写少
6,公平锁和非公平锁:减少了线程的阻塞唤醒
7. 悲观锁

三丶加锁的本质

加锁实际上就是去竞争一个同步状态
例如:使用 state= 0/1 表示(无锁(0)和有锁(1)) 锁的状态,当一个线程获取到锁时 将 state 修改成 1 表示加锁成功,下一个线程访问时 发现 state=1 那么它就无法加锁 不能进行资源访问和操作 进入阻塞状态.
加锁底层: 通过竞争操作系统层面的Mutex的机制去实现一个互斥状态的处理. 多线程去竞争Mutex锁的时候涉及到内核指令去调用的这个机制,但是存在用户态到内核态的切换占用cpu资源会消耗性能.进行线程切换时 需要将当前线程阻塞,同时保存当前线程执行的指令的上下文信息保存起来
加锁造成性能问题的原因:

1.竞争同步状态的时候,设计上下文切换
2.线程的阻塞和唤醒,也会涉及到切换
3.并行到串行的改变 

四丶Java各种锁

1.自选锁(轻量级锁):线程在竞争不到锁,进入阻塞状态等待之前,进入循环去重试加锁来避免进行阻塞状态. 轻微锁竞争
2.偏向锁:加锁代码不存在竞争,锁的竞争必要性就不存在,将锁偏向于当前线程,当线程下次进入时不需要竞争锁 不存在锁竞争
3.重量级锁:依赖于操作系统的互斥量(mutex) 实现。大量锁竞争
4.锁消除:编译器优化 当前代码加锁导致无效竞争,jvm在编译时将锁消除掉
5.锁膨胀: 控制锁的粒度太小导致加锁频繁,jvm将锁的粒度变大.
6.读写锁(读多写少): 读操作不会修改资源,读和读不竞争锁,读和写竞争锁,写和写竞争锁.
7.乐观锁:乐观锁在操作数据时非常乐观,认为别人不会同时修改数据。因此乐观锁不会上锁,只是在执行更新的时候判断一下在此期间别人是否修改了数据:如果别人修改了数据则放弃操作,否则执行操作。
8.悲观锁:悲观锁在操作数据时比较悲观,认为别人会同时修改数据。因此操作数据时直接把数据锁住,直到操作完成后才会释放锁;上锁期间其他人不能修改数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

fly_fly_good

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

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

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

打赏作者

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

抵扣说明:

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

余额充值