Java锁及其应用

一、乐观锁和悲观锁
乐观锁:不加锁,但是依据是否有被修改过来判断失败与否,适用于大量读取的场景。乐观锁的实现:CAS、java.uil。concurrent.atomic包下面的类。

悲观锁:加锁,锁住资源不让其他线程操作,保证只有占有锁的线程去更新资源。适用于大量写入的场景。悲观锁的实现:Synchronzed、ReentrantLock.

关于悲观锁的实现前面我们已经详细学习过了,这里主要介绍一下乐观锁的CAS。

CAS是compare and swap的简称,即先比较后设置,CAS是一个原子操作。使用场景是更新一个值,不依赖于加锁实现,可以接收CAS失败。局限性是只可以更新一个值,如AtomicReference,AtomicInterger需要同时更新时,无法做到原子性。

注:AtomicStampedReference处理ABA问题是通过比较时间来处理的。

二、数据库乐观锁和悲观锁
1、mysal数据库悲观锁(innodb引擎)
数据库的锁都是在事务里面实现的

select…lock in share mode
eg:select * from tableA where id =1 lock in share mode
a)是共享锁,在事务内生效
b)给符合条件的行添加的是共享锁,其他事务会话同样可以继续给这些行添加共享锁,在锁释放前其他事务无法对这些行进行删除和修改。
c)两个事务同时对一行加共享锁,无法更新,直到有一个事务对该行加共享锁。
d)如果两个加了共享锁的事务同时更新一行,会发生DeadLock死锁问题。
e)某行已有排他锁,无法继续添加共享锁。
f)不会阻塞正常读。
结论:享锁,事务都加,都能读。修改是惟一的,必须等待前一个事务 commit,才可

select …for update
a)排他锁,在事务内生效。
b)给符合条件的行添加的是排他锁,其他事务无法再加排他锁,在释放前,其他事务无法对这些行进行删除和修改。
c)某行已有共享锁,无法再加排他锁
d)第一个事务对某行加了排他锁,第二个事务继续加排他锁,第二个事务需要等待。
e)加锁有超时时间
f)不会阻塞正常读

总结
事务之间不允许其它排他锁或共享锁读取,修改更不可能
一次只能有一个排他锁执行 commit 之后,其它事务才可执行
不允许其它事务增加共享或排他锁读取。修改是惟一的,必须等待前一个事务 commit。

三、AQS的数据结构
AQS:AbstractQueuedSynchronizer,它提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架,ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier等并发类均是基于AQS来实现的,具体用法是通过继承AQS实现其模板方法,然后将子类作为同步组件的内部类。

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

具体看下面两张图的介绍
在这里插入图片描述
waitStatus
四、ReentrantLock

简单加锁解锁过程
ReentrantLock简单加锁解锁过程

可重入实现
可重入实现
公平锁和非公平锁
在这里插入图片描述

四、并发容器及原理分析
1、HashMap的实现原理
这部分网上资源很多,讲解的也很详细,这里贴出几个
jdk1.8 HashMap工作原理和扩容机制(源码解析)
Java中HashMap底层实现原理(JDK1.8)源码分析
Hashmap的结构,1.7和1.8有哪些区别,史上最深入的分析
2、HashMap在高并发场景下的死锁分析
并发场景下由于在扩容中的每一个操作都有可能被操作系统挂起,当挂起的是扩容转换中的操作的时候,多线程会造成每个线程的操作数据混合在同一个数据结构中。
所以并发场景下使用高并发场景下的HashMap:ConcurrentHashMap

3、ConcurrentHashMap和HashMap的数据结构类似,但还是有一些差别,看图
ConcurrentHashMap存储结构
Segment继承ReentrantLock,具有加锁解锁的功能,segment有多少个元素就说明有多少把锁,扮演了分段锁,降低并发竞争。
只有写才会对对应的segment加锁,读操作不加锁。

其实和 1.8 HashMap 结构类似,当链表节点数超过指定阈值的话,也是会转换成红黑树的,大体结构也是一样的。

那么它到底是如何实现线程安全的?
答案:其中抛弃了原有的Segment 分段锁,而采用了 CAS + synchronized 来保证并发安全性。至于如何实现,那我继续看一下put方法逻辑

/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
   
    if (key == null || value == null) throw new NullPointerException();
    //1. 计算key的hash值
    int hash = spread
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值