Java必看面试题——同步、锁(持续更新)

先说说造成死锁的原因有哪些

  1. 当前线程拥有其他线程需要的资源
  2. 当前线程等待其他线程已经拥有的资源
  3. 都不放弃自己拥有的资源

Java并发的线程同步器有哪些?

Exchanger
Semaphore
CountDownLatch
CyclicBarrier
Phaser


说说多线程同步器 CountDownLatch、CyclicBarrier、Semaphore、Condition、Phaser、Exchanger使用场景

  • CountDownLatch
    场景:等待一组线程任务完成后在继续执行当前线程。
    用法:定义一个CountDownLatch变量latch,在当前线程中调用latch.await()方法,在要等待的一组线程中执行完后调用latch.countDown()方法,这样当该做线程都调用过latch.countDown()方法后就开始执行当前线程latch.await()后的方法。
    不足:CountDownLatch是一次性的,计算器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。
  • CyclicBarrier
    场景:等待一组线程到达某个点后一起执行,该组线程达到指定点后可以再次循环执行。也可用于一组线程达达某个点后再执行某个方法。
    用法:定义一个CyclicBarrier变量barrier,线程达到某个约定点时调用barrier.await()方法,当该组所有线程都调用了barrier.await()方法后该组线程一起向下执行。
    举例:在分组计算中,每个线程负责一部分计算,最终这些线程计算结束之后,交由屏障线程进行汇总计算。
  • CyclicBarrier和CountDownLatch的区别
    CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
  • Semaphore
    场景:对于一组有限制都资源访问。比如餐厅有5个位置但同时有7个人要吃饭,则要控制7个人对餐位的并发实用。
    用法:定义Semaphore变量semaphore包含受限的资源个数,每个人要来用餐时先调用semaphore.acquire()方法获取一个餐位(若没有餐位,则阻塞等待),用完餐后调用semaphore.release()释放餐位给其它人用。Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量(synchronized 只能控制单一线程),就这一点而言,单纯的synchronized 关键字是实现不了的。
    例子:公平锁,(先来先到)
  • Condition
    控制线程的“等待”和“唤醒”,Object.wait()的升级版
  • Phaser
    和 CyclicBarrier 类似
  • Exchanger
    让两个线程在合适时交换数据
    适用场景:当两个线程工作在同一个类的不同实例上时,用于交换数据
    解决了什么问题:线程间高效交换数据

Atomic包下提供的能原子更新数组中元素的类有哪些?

AtomicReferenceArray
AtomicIntegerArray
AtomicLongArray
AtomicReference(不包括)


AtomicBoolean 本身具有原子性吗?

属于java.util.concurrent.atomic包下面的
原理是基于volatile关键字加上CAS算法,CAS操作是原子性的


AtomicBoolean 内部方法都是原子性的吗?

首先要知道内部有哪些方法:内部的value本身就是一个volatile关键字修饰的成员属性,方法:

  • compareAndSet(boolean expect, boolean update):对比并且设置boolean最新的值,类似于AtomicInteger的compareAndSet方法,期望值与Atomic Boolean的当前值一致时执行新值的设置动作,若设置成功则返回true,否则直接返回false。
  • weakCompareAndSet(boolean expect, boolean update):同上。
  • set(boolean newValue):设置AtomicBoolean最新的value值,该新值的更新对其他线程立即可见。
  • getAndSet(boolean newValue):返回AtomicBoolean的前一个布尔值,并且设置新的值。

成员属性是vloatile修饰的,所以对应的方法应该都是原子性的


怎么理解原子性?

Java中,对基本数据类型的读取和赋值操作是原子性操作,所谓原子性操作就是指这些操作是不可中断的,要做一定做完,要么就没有执行。

比如:
i=2是读取操作,必定是原子性操作,
j=i你以为是原子性操作,其实吧,分为两步,一是读取i的值,然后再赋值给j,这就是2步操作了,称不上原子操作,
i++和i = i + 1其实是等效的,读取i的值,加1,再写回主存,那就是3步操作了。


基于Happens——before规则,处理并发时优先使用哪个

BlockingQueue
相比Synchronized和lock


resume和suspend废弃原因

调用suspend后不会释放锁
如果线程A暂停后,他的resume是由线程B调用的,但是B又依赖A的某个锁,就会死锁

  • suspend() 和 resume() 方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的 resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情 形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。但suspend()方法很容易引起死锁问题, 已经不推荐使用了。
  • wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许 指定以毫秒为单位 的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应 的 notify() 被调用。

哪些会使线程进入阻塞状态

wait
sleep
join
yield(不会)


哪些接收了Thread.interrupt()会抛出InterruptedException

sleep
wait
join
java.nio.channels.SocketChannel的阻塞方法
java.net.Socket阻塞方法(不会)

  • 调用了Thread.interrupt()会请求另外一个线程终止执行,而不是直接终止
  • 当检查到线程被interrupt后,应抛出InterruptedException,并在finally或try-with-resources中清理

可重入锁

synchronized、ReentrantLock


常见线程池的差异

  • newCachedThreadPool:可缓存线程的线程池,适用于并发不固定的短期小任务
  • newFixedThreadPool:创建一个固定大小的线程池,可控制线程最大并发数,超出的线程会在队列中等待
  • newWorkStealingPool:适合用在很耗时的操作,但不是ThreadPool的扩展,是ForkJoinPool的扩展,是在一个Executors类中实现,可以合理使用CPU
  • newSingleThreadExecutor:单线程池,时候串行任务

类锁和设备锁区别

类锁和设备锁区别

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值