JUC并发编程面试题(自用)

线程池

1 线程池的作用:提高线程的利用率,线程复用,频繁的创建和销毁线程非常浪费资源

线程池的七大参数:

  1. corePoolSize(核心线程数):线程池中始终保持的活动线程数,即使它们处于空闲状态也不会被回收。

  2. maximumPoolSize(最大线程数):线程池中允许存在的最大线程数。当工作队列满了且核心线程已经都在工作时,线程池会创建新的线程,但不会超过这个值。

  3. keepAliveTime(线程空闲时间):当线程池中的线程数量超过核心线程数时,多余的空闲线程在空闲时间达到一定值后会被终止。

  4. unit(线程空闲时间的单位):用于指定keepAliveTime的时间单位,例如秒、毫秒等。

  5. workQueue(工作队列):用于存放等待执行的任务的队列,可以是有界队列或无界队列,如LinkedBlockingQueueArrayBlockingQueue等。

  6. threadFactory(线程工厂):用于创建线程的工厂,通常用来自定义线程的名称、优先级等。

  7. handler(拒绝策略):当工作队列已满且线程池中的线程数量达到最大线程数时,新任务的处理策略,有四种常见的策略(下文有详细介绍)。

四种常见的拒绝策略:

  1. ThreadPoolExecutor.AbortPolicy(默认策略):当工作队列已满且线程池中的线程数量达到最大线程数时,抛出RejectedExecutionException异常,拒绝新任务的提交。

  2. ThreadPoolExecutor.CallerRunsPolicy:当工作队列已满时,该策略会将任务返回给调用者(即当前线程),这样任务提交者将自己执行任务,不会被丢弃。

  3. ThreadPoolExecutor.DiscardOldestPolicy:当工作队列已满时,该策略将丢弃队列中最旧的任务(即队列头部的任务),然后尝试重新提交当前任务。

  4. ThreadPoolExecutor.DiscardPolicy:当工作队列已满时,该策略会默默地丢弃新的任务,不会抛出异常也不会执行任务

过程:任务加入线程池后,先判断核心线程数是否达到最大,如果达到,加入任务队列,如果任务队列已满,开启非核心线程,如果达到最大线程数,开始执行拒绝策略。

关闭线程池:shutdown与shutdownnow的区别,前者决绝任务的提交,把之前任务队列中未完成的任务完成之后再关闭 ,后者直接关闭所有正在运行的任务。

如何设置最大线程数:cpu密集型,设置为cpu的核数,Io密集型,两倍cpu核数

为什么要自定义线程池参数?

我们发现newFixedThreadPool和newSingleThreadExecutor方法他们都使用了LinkedBlockingQueue的任务队列,LikedBlockingQueue的默认大小为Integer.MAX_VALUE。而newCachedThreadPool中定义的线程池大小为Integer.MAX_VALUE。

所以阿里禁止使用Executors创建线程池的原因就是FixedThreadPool和SingleThreadPool的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。

CachedThreadPool允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

synchronized 

原理:

synchronized 是 Java 中用于实现同步的关键字,它的原理涉及到 Java 对象头和对象监视器的概念。以下是 synchronized 的基本原理:

  1. Java 对象头:每个 Java 对象在内存中都有一个对象头,其中包含了对象的元数据信息,如哈希码、GC 分代信息等。这些元数据存储在对象头中,占用一定的内存空间。

  2. 对象监视器(Monitor):Java 对象头中的一部分用于实现同步,被称为对象监视器或锁。每个对象都有一个关联的对象监视器,它用于控制对对象的并发访问。对象监视器可以被锁定(locked)和解锁(unlocked)。

 Synchronized的语义底层是通过一个monitor(监视器锁)的对象来完成,
每个对象有一个监视器锁(monitor)。每个Synchronized修饰过的代码当它的monitor被占用时就
会处于锁定状态并且尝试获取monitor的所有权 ,过程:
1.如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为
monitor的所有者。
2.如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再
重新尝试获取monitor的所有权。
 

特性

可重入性:一个拿到锁的线程可以多次进入同步方法中,比如在同步方法中调用另一个该锁对象的同步方法,底层使用计数器实现1.如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为
monitor的所有者。
2.如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再
重新尝试获取monitor的所有权。

不可中断性:线程进入同步方法后,其他线程只能阻塞等待,不能中断拿到锁的进入同步方法里面的线程

synchronized 中四种锁的升级

在 Java 中,synchronized 关键字支持多种不同的锁升级策略。这些策略使锁能够在多线程环境下有效地保护共享资源,同时最大程度地减少线程间的竞争和锁的开销。

      0   无状态锁:没有任何线程进入同步代码块

  1. 偏向锁(Biased Locking):偏向锁是为了优化单线程场景而引入的。在单线程环境中,无需进行复杂的竞争,所以第一个获得锁的线程会获得偏向锁,之后再次进入同步块时,无需竞争,直接获得锁。只有在有其他线程争用锁的情况下,偏向锁才会升级为轻量级锁。

  2. 轻量级锁(Lightweight Locking):轻量级锁是为多线程场景优化的锁升级策略。当多个线程尝试争夺锁时,JVM会将偏向锁升级为轻量级锁。轻量级锁使用 CAS 操作(比较并交换)来尝试获取锁,而不是阻塞线程。如果获取锁的尝试成功,那么线程可以快速进入临界区。如果失败(轻量级锁会在达到一定的cas次数阈值之后升级为重量级锁),它会升级为重量级锁。

  3. 重量级锁(Heavyweight Locking):重量级锁是最传统的锁升级策略,当多个线程争夺锁时,会导致其他线程阻塞,直到获得锁的线程释放锁。这种锁升级策略涉及到操作系统层面的线程阻塞和唤醒,因此性能开销相对较高。

synchronized 与Lock锁的区别

1 synchronized 是一个关键字,Lock是一个接口

2 synchronized 是非公平锁,Lock可以实现公平锁

3 synchronized 会自动释放锁,Lock必须手动加锁与释放锁

4 在线程之间通信时synchronized 无法精准唤醒

5 lock锁更灵活的锁住代码

什么是自旋
很多 synchronized 里面的代码只是一些很简单的代码,执行时间非常快,此时等待的线程都加锁
可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切换的问题。既然
synchronized 里面的代码执行得非常快,不妨让等待锁的线程不要被阻塞,而是在 synchronized
的边界做忙循环,这就是自旋。如果做了多次循环发现还没有获得锁,再阻塞,这样可能是一种更
好的策略。
 

CAS

cas是unsafe包下的一个方法。基于乐观锁的实现机制。原理是:内存值 预期值 新值,如果内存值与预期值相等,就把新值赋值给内存值,否则失败。cas用于轻量级锁。

i++的同步操作:1 获取i的内存值  2 A=i+1  3 i=A 

cas是实现第三步

cas导致的问题:

1、ABA 问题:
比如说一个线程 one 从内存位置 V 中取出 A,这时候另一个线程 two 也从内存中取出 A,并且 two 进行了一些操作变成了 B,然后 two 又将 V 位置的数据变成 A,这时候线程 one 进行 CAS 操作发现内存中仍然是 A,然后 one 操作成功。尽管线程 one 的 CAS 操作成功,但可能存在潜藏的问题。从Java1.5 开始 JDK 的 atomic包里提供了一个类 AtomicStampedReference 来解决 ABA 问题。问题:取钱,多按了几次取50的操作,总共100,第一次成功变为50,如果有人转账给你50,现在卡里有100,第二次取钱操作也会执行成功,变为50,造成数据不一致问题
2、循环时间长开销大:
对于资源竞争严重(线程冲突严重)的情况,CAS 自旋的概率会比较大,从而浪费更多的 CPU 资源,效率低于 synchronized。
3、只能保证一个共享变量的原子操作:
当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个共享变量操作时,循环 CAS 就无法保证操作的原子性,这个时候就可以用锁。

Volatile:

可见性、有序性

ThreadLocal

ThreadLocal的使用_threadlocal如何使用-CSDN博客


 

猜你喜欢

转载自blog.csdn.net/qq_52135683/article/details/133885980