解析Java中的部分锁

1.乐观锁和悲观锁

定义

  • 乐观锁认为自己在使用数据的时候不会有别的线程修改数据,所以不会添加锁。在更新数据的时候去判断有没有别的线程更新数据。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。
  • 悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,所以在使用数据之前一定会加锁。

适用条件
从上面的定义可以发现

  • 乐观锁适用于读多写少的场景。
  • 悲观锁适用于写操作多的场景,保证写操作数据正确,避免乐观锁的多次自旋。

示例代码
下面是java中使用悲观锁和乐观锁的示例

// ------------------------- 悲观锁的调用方式 -------------------------
// synchronized
public synchronized void testMethod() {
	// 操作同步资源
}
// ReentrantLock
private ReentrantLock lock = new ReentrantLock(); // 需要保证多个线程使用的是同一个锁
public void modifyPublicResources() {
	lock.lock();
	// 操作同步资源
	lock.unlock();
}

// ------------------------- 乐观锁的调用方式 -------------------------
private AtomicInteger atomicInteger = new AtomicInteger();  // 需要保证多个线程使用的是同一个AtomicInteger
atomicInteger.incrementAndGet(); //执行自增1

2.自旋锁、适应性自旋锁

自旋锁: 阻塞或唤醒一个Java线程需要CPU状态的切换,这种切换可能会比我们程序执行的时间都要长。
在多处理器计算机普及的现在,能够让两个或以上线程并行运行,我们可以让后面没有获取锁的线程不放弃CPU的使用权,而是进行自旋获取锁。等到获取锁的时候,可以直接获取资源不需要额外多出CPU切换的耗时。
缺点: 如果锁被占用时间太长,自旋时间会很长,会浪费处理器的资源。
解决: 使用-XX:PreBlockSpin 参数调节最大自旋字数,超过就挂起当前线程。
在这里插入图片描述

自适应自旋锁: 自旋的时间(次数)不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也是很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。如果对于某个锁,自旋很少成功获得过,那在以后尝试获取这个锁时将可能省略掉自旋过程,直接阻塞线程,避免浪费处理器资源。

3.无锁、偏向锁、轻量级锁、重量级锁

无锁: 无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。
CAS则是无锁。

偏向锁
定义: 偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。
大多数情况锁只会被一个线程多次获得,所以有了偏向锁。在进入和退出同步代码块时不需要CAS操作来加锁和解锁,只需要判断Mark Word 中偏向线程的ID是否是当前线程。

偏向锁的撤销:偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动释放偏向锁。

当持有偏向锁的线程已经退出同步代码块,这是对象转为不可偏向的无锁状态。
当持有偏向锁的线程还在同步代码块,偏向锁依旧有效,此时对象就应该转换为轻量级的加锁状态。
偏向锁的关闭:-XX:-UseBiasedLocking=false,之后默认进入轻量级锁。

轻量级锁: 是指当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。

轻量级锁的获取:在进入同步代码块的时候对象的Mark Word是否无锁状态 --是–> 在虚拟机的当前线程的栈帧简历一个Lock Record,将对象头中的Mark Word拷贝到Lock Record中 --> 拷贝成功后,CAS将对象的Mark Word 指向当前线程的Lock Record。(此时已经获取轻量级锁成功。) , 并将Lock Record 的owner指针指向对象的Mark Wrod

如果CAS更新失败,则会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁(是否拥锁,看的是Mark Word指向的哪个线程的栈帧),那就可以直接进入同步块继续执行,否则说明有多个线程竞争锁。
升级为重量级锁: 满足以下一个条件就可以。

  • 当前只有一个线程自旋等待,当自旋超过一定次数。
  • 当持有轻量级锁的线程CAS替换Mark Word的时候,如果失败了。则升级重量级锁。

升级重量级锁这块有很多博客、包括《Java并发编程的艺术》没有讲全。

重量级锁
升级为重量级锁时,锁标志的状态值变为“10”,此时Mark Word中存储的是指向重量级锁的指针,此时等待锁的线程都会进入阻塞状态。

重量级锁就是原生的Synchronized,他依赖操作系统的Mutex Lock来实现的。系统调用时需要从用户态切换到内核态。所以是非常耗时间的。

综上,偏向锁通过对比Mark Word解决加锁问题,避免执行CAS操作。而轻量级锁是通过用CAS操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。重量级锁是将除了拥有锁的线程以外的线程都阻塞

4.公平锁、非公平锁

定义: 简单来说,公不公平指的是能否插队。

在公平锁中,只有一次机会可以获取到锁,就是从队列的后面排队等到前一个的线程执行完成唤醒自己。
在这里插入图片描述
非公平锁中,则有两次机会。

  • 在进入队列前,如果正好是队首线程执行完毕,可以正好将自己插到队首执行。
  • 从队列的后面排队等到前一个的线程执行完成唤醒自己。
    在这里插入图片描述
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值