Java并发(五)——锁

1 死锁

死锁是指两个或两个以上的线程在执行任务过程中,因争夺资源而造成的相互等待的现象

产生死锁的条件:

  • 互斥条件:指线程对已经获取的资源进行排他性使用,即该资源同时只能由一个线程占用
  • 请求并持有条件:指线程已经持有了至少一个资源,但又请求其他资源,如果请求不到则阻塞,但不释放已有的资源
  • 不可剥夺条件:指线程获取到资源后,在使用完之前不能被其他线程占用
  • 环路等待条件:指发生死锁时,必然存在一个线程——资源的环形链

避免死锁:破坏死锁产生的条件

  • 破坏请求并持有条件:一次性获取所有所需的资源
  • 破坏不可剥夺条件:占用资源的线程再获取其他资源获取不到时,释放已有的资源
  • 破坏环路等待条件:按某一顺序申请资源,释放资源则按相反的顺序

2 锁的类型

2.1 乐观锁和悲观锁

悲观锁:指对数据被外界的修改持悲观态度,认为数据很容易被修改。因此操作前会先进行加锁,操作结束再释放锁。悲观锁的实现往往依靠数据库提供的锁机制

乐观锁:与悲观锁相反。因此在访问数据时不会加锁,只有对数据提交更新的时候,才会检测数据是否冲突。乐观锁通常通过version字段(CAS)或者使用业务状态来实现

2.2 公平锁和非公平锁

公平锁:线程获取锁的顺序是按照线程请求锁的顺序

非公平锁:通过抢占的方式来争夺锁

2.3 独占锁和共享锁

独占锁:一种悲观锁,保证任何时候仅有一个线程可以获得锁

共享锁:一种乐观锁,允许多个线程同时进行读操作

2.4 读写锁和排他锁

排他锁:同一时刻只允许一个线程访问

读写锁:内部维护一个读锁一个写锁,通过分离读锁和写锁,在读多写少情况下提高性能

2.5 可重入锁

当线程再次获取一个自己已经获取的锁时,如果不会被阻塞,则该锁是可重入锁

原理:在锁内部维护一个标识,标识哪个线程占用,一个计数器,计数该线程获取锁的次数

2.6 自旋锁

当线程在获取锁时,如果发现锁已经被占用,不会马上阻塞自己,而是不放弃CPU使用权的情况下,多次尝试获取

3 锁的等级

3.1 偏向锁

偏向锁在资源无竞争情况下消除了同步语句,连CAS操作都不做了,提高程序的运行性能

一个线程在进入同步块时,会检查锁的MarkWord里是不是自己的线程ID

如果是,则表明该线程已经获取偏向锁,以后该线程进入和退出同步块不需要花费CAS操作加锁和解锁

如果不是,则代表有另一个线程竞争偏向锁,尝试用CAS来修改MarkWord中的线程ID。如果成功,表示另一个线程不存在了(释放锁了),如果失败,则升级为轻量级锁

在这里插入图片描述

3.2 轻量级锁

线程尝试用CAS修改MarkWord中的线程ID,如果失败,则采用自旋来获取锁

如果自旋到一定程度,依然没有获取锁,则升级为重量级锁

在这里插入图片描述

3.3 重量级锁

重量级锁依赖于操作系统中的互斥量来实现

3.4 总结

偏向锁

  • 优点:加锁和解锁不需要额外的消耗
  • 缺点:如果线程间存在锁竞争,则会带来额外的锁撤销的消耗
  • 适用:只有一个线程访问同步块

轻量级锁

  • 优点:竞争的线程不会阻塞,提高了程序的响应速度
  • 缺点:自旋会消耗CPU
  • 适用:追求相应时间,同步块执行速度非常快

重量级锁

  • 优点:不使用自旋,不会消耗CPU
  • 缺点:线程阻塞,响应时间缓慢
  • 适用:追求吞吐量,同步块执行时间较长
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值