面经系列:java并发编程(3)

  • Java中锁的分类

  • 可重入锁、不可重入锁

可重入锁:synchronized、reentrantLock、reentrantReadWriteLock

不可重入锁:Worker

  • 乐观锁、悲观锁

悲观锁:synchronized、reentrantLock、reentrantReadWriteLock

乐观锁:CAS

  • 公平锁、非公平锁

Synchronized只能是非公平锁

ReentrantLock、reentrantReadWriteLock可以实现公平锁和非公平锁。

  • 互斥锁、共享锁

Synchronized、reentrantLock是互斥锁

ReentrantReadWriteLock可以实现互斥锁和共享锁

  • Synchronized的优化

锁消除:不存在操作临界资源时锁被优化掉

锁膨胀:频繁进行锁获取和释放,会将锁扩大范围

锁升级:synchronized在jdk1.6之前,如果获取不到锁,就会挂起当前线程。

Jdk1.6做了锁升级优化:

无锁、匿名偏向:当前对象没有作为锁存在

偏向锁:如果当前锁资源只存在一个线程在频繁获取和释放,那么这个线程来只需要判断线程是否是当前线程。如果是,直接拿走锁资源;如果不是,基于CAS尝试将偏向锁指向当前进程。如果获取不到,触发锁升级,锁升级为轻量级锁。(也就是说,偏向锁出现竞争就触发锁升级)

轻量级锁:频繁的CAS尝试(自适应自旋锁),如果成功获取,拿到锁资源。自旋到一定次数还没有获取到锁资源,触发锁升级。

重量级锁

  • Synchronized的实现原理

Synchronized同步语句块的实现使用的是monitorenter monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。

执行monitorenter流程:

对象锁的拥有者线程才可以执行monitorexit指令释放锁,monitorexit执行流程:

  • Synchronized和lock的区别

Synchronized和lock都是java中用来解决线程安全的工具。

特性:synchronized是java中的同步关键字;lock是juc包提供的一个接口,这个接口有很多实现类,比如reentrantlock。

锁的粒度:Synchronized可以通过修饰在方法上或修饰在代码块上来控制锁的粒度。还可以通过加锁对象的生命周期来控制锁的作用范围,如锁对象是类对象,那么锁的全局锁,如果是实例对象,则锁范围取决于实例的生命周期;Lock的锁粒度通过它提供的lock()unlock()方法实现。

灵活性:synchronized简单易用,但功能基础。它是不可中断锁;lock提供了更高级的功能,如可中断锁(lockInterruptibly())、尝试非阻塞地获取锁(tryLock())、尝试在给定最大时间内获取锁(tryLock(long time, TimeUnit unit)),以及支持公平锁等。

条件变量支持:synchronized:需要配合Object类的wait()、notify()和notifyAll()方法实现等待/通知机制;Lock:提供了Condition接口来实现分组唤醒需要唤醒的线程,可以更精细地控制多线程的等待与通知。

名词解释:

不可中断:如果一个线程在等待获取synchronized锁时被阻塞,它不能被中断,即不能响应中断请求。这意味着,该线程必须等到它之前的线程释放锁之后才能继续执行,这在某些情况下可能会导致问题(你想让它因为某些原因(如超时、任务取消等)放弃等待,也做不到,它会一直等下去,直到获取到锁),比如死锁或长时间等待。

可中断:Lock接口提供了lockInterruptibly()方法,允许线程在等待锁的过程中响应中断。这意味着,如果一个线程A在等待获取锁时被中断,它可以退出等待锁的状态,进而处理中断请求,这为处理长时间等待或死锁提供了一种可能的解决方案。

  • synchronized和reentrantLock的区别(reentrantLock是lock的一个实现类,所以这个问题的回答与上题基本一致)

synchronized是java关键字,提供了一种隐式的锁管理机制,锁一个对象,当一个线程进入synchronized方法或代码块时,自动获取锁,退出时自动释放锁。ReentrantLock是juc包下的一个类,提供了一种显示锁管理机制,需要手动获取和释放锁。

synchronized 比较简单,功能相对有限,主要用于实现方法或代码块的同步。

ReentrantLock 提供了比synchronized更丰富的功能,例如尝试非阻塞地获取锁(tryLock())、可中断地获取锁(lockInterruptibly())、支持公平锁等。

条件变量支持:

synchronized 与Object类中的wait()、notify()和notifyAll()方法结合使用,来实现等待/通知机制。

ReentrantLock 使用Condition接口来提供等待/通知机制,每个ReentrantLock可以关联一个或多个Condition对象,这提供了分组唤醒等待线程的能力,比synchronized的等待/通知机制更加灵活和强大。

  • 什么是AQS

AbstractQueuedSynchronizer抽象类,AQS是juc包下的一个基类,juc下的很多内容都是基于AQS实现了部分功能,如ReentrantLock、TreadPoolExecutor、阻塞队列、CountDownLatch、Semaphore等。

AQS的核心思想:如果被请求的资源空闲,则将请求该资源的线程设为工作线程,并将该资源设定为锁定状态。如果被请求的资源被占用,则需要一套线程阻塞和唤醒的锁分配机制。AQS中这个机制是基于CLH锁实现的。

AQS原理:

首先,AQS提供了一个由volatile修饰,并采用CAS方式修改的int型state变量。

其次,AQS维护了一个双向链表,有head,有tail,并且每个节点都是Node对象,并维护双向链表插入操作。暂时获取不到锁的线程被封装成一个Node,在双向链表(CLH队列)中排队。

  • AQS如何添加结点和唤醒结点?

节点是从链表的尾部加入的,这个过程主要是通过enq方法实现的,确保线程安全性是通过CAS操作完成的。而唤醒操作则是针对位于头部后的节点进行的。因为头节点通常是已经执行完毕或正在执行的节点。这种设计允许AQS以公平和高效的方式管理等待的线程,确保每个线程都能有序地获取同步状态。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值