type
status
date
slug
summary
tags
category
icon
password

1、synchronized 的实现原理以及锁优化?

monitor 偏向锁 轻量级锁 重量级锁 对象由三部分组成 对象头 实例数据 对齐填充 可重入锁 自适应自旋锁

2、ThreadLocal原理,使用注意点,应用场景有哪些?

用完之后remove 线程独有变量 分页插件

3、synchronized和ReentrantLock的区别?

synchronized 通过监视器锁实现 monitorenter monitorexit 悲观锁 lock 乐观锁 CAS

4、说说CountDownLatch与CyclicBarrier 区别

countdownlatch 一个或者多个线程等待n个线程完成操作之后才能执行 cyclicbarriet 循环等待一组线程执行完毕后开始执行后面的操作

5、Fork/Join框架的理解

分而治之 fork 将任务添加到队列 join 窃取任务执行

6、为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?

从底层代码可以看到 start调用了一个native方法start0,他是实现多线程的关键,将线程变为可执行状态,具体什么时候调用取决于cpu 而run方法只是一个普通的方法

7、Java中的volatile关键是什么作用?怎样使用它?在Java中它跟synchronized方法有什么不同?volatile 的实现原理

保证可见性 依赖缓存一致性协议 有序性 防止指令重排依赖内存屏障实现 LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。 StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。 LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被执行前,保证Load1要读取的数据被读取完毕。 StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。 底层会对总线加锁 happens-before 不在这八条规则里面都可以进行指令排序 不保证原子性 MESI缓存一致性协议 状态 描述 说明 M(modify) 修改 当前CPU刚修改完数据的状态,当前CPU拥有最新数据,其他CPU拥有失效数据,而且和主存数据不一致 E(exclusive) 独占 只有当前CPU中有数据,其他CPU中没有改数据,当前CPU的数据和主存的数据是一致的 S(shared) 共享 当前CPU和其他CPU中都有共同的数据,并且和主存中的数据一致 I(invalid) 失效 当前CPU中的数据失效,数据应该从主存中获取,其他CPU中可能有数据也可能无数据;当前CPU中的数据和主存中的数据被认为不一致。

8、CAS?CAS 有什么缺陷,如何解决?

CAS的缺点: 1.CPU开销较大 在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。 2.不能保证代码块的原子性 CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。 3.ABA问题 这是CAS机制最大的问题所在。 什么是ABA问题? 引用原书的话:如果在算法中的节点可以被循环使用,那么在使用“比较并交换”指令就可能出现这种问题,在CAS操作中将判断“V的值是否仍然为A?”,并且如果是的话就继续执行更新操作,在某些算法中,如果V的值首先由A变为B,再由B变为A,那么CAS将会操作成功。 怎么避免ABA问题? Java中提供了AtomicStampedReference和AtomicMarkableReference来解决ABA问题。

9、如何检测死锁?怎么预防死锁?死锁四个必要条件

jstack 查看线程日志中的deadlock 打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。   ● 打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。   ● 打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。   ● 打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源 ● 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。   ● 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。   ● 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。   ● 循环等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。   这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁

10、如果线程过多,会怎样?

消耗过多的cpu oom

11、说说 Semaphore原理?

信号量 维护许可证的数量 控制并发的线程 底层原理是AQS

12、AQS组件,实现原理

提供了一些模版方法 AQS的两种功能 独占 ReentrantLock 共享 ReentrantReadWriteLock Node 线程获取锁,如果获取了锁就 保存当前获得锁的线程,如果没获取就创造一个节点通过compareAndSetTail(CAS操作)操作的方式将创建的节点加入同步队列的尾部,在同步队列中的节点通过自旋的操作不断去获取同步状态【当然由于FIFO先进先出的特性】等待时间越长就越先被唤醒。当头节点释放同步状态的时候,首先查看是否存在后继节点,如果存在就唤醒自己的后继节点,如果不存在就获取等待时间最长的符合条件的线程

12、假设有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?

创建三个countdownlatch T1执行完 latch1 release T2执行完, latch2 release T3执行完 latch3 release

13、LockSupport作用是?

线程唤醒 unpark 阻塞 park

14、Condition接口及其实现原理

多线程间协调通信 await signal

15、说说并发与并行的区别?

并行是指同时进行 并发不是同时进行,依赖于底层cpu的调度

16、为什么要用线程池?Java的线程池内部机制,参数作用,几种工作阻塞队列,线程池类型以及使用场景

节省线程创建与销毁的开销

17、如何保证多线程下 i++ 结果正确?

加锁

18、10 个线程和2个线程的同步代码,哪个更容易写?

都不容易写

19、什么是多线程环境下的伪共享(false sharing)?

伪共享发生在不同处理器上的线程对变量的修改依赖于相同的缓存行 L1 L2 L3 每一级高速缓存都是由多个缓存行组成的 当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享 处理伪共享的两种方式: 字节填充:增大元素的间隔,使得不同线程存取的元素位于不同的cache line上,典型的空间换时间。 字节填充注解 @contended1.8 在每个线程中创建对应元素的本地拷贝,结束后再写回全局数组。

20、线程池如何调优,最大数目如何确认?

看业务需求来定义 cpu密集型 io密集型

21、Java 内存模型?

主内存和工作内存 由于无法直接读取主内存中的变量,先把主内存中的变量拷贝到工作内存,修改之后刷新到主内存

22、怎么实现所有线程在等待某个事件的发生才会去执行?

countdownlatch

23、说一下 Runnable和 Callable有什么区别?

runnable 没有返回值 自行处理异常 callable 有返回值 future.get捕获异常

24、线程的生命周期,线程的几种状态。

新建状态 新建线程 就绪状态 调用start方法 准备好等待cpu调度(不一定立马执行) 运行状态 cpu开始调度进行执行 阻塞状态 等待阻塞 调用wait 同步阻塞 要获取的锁已经被其他线程占用 其他阻塞 执行sleep 其他线程调用join方法 死亡状态 线程执行完毕或者因异常退出

25、ReentrantLock实现原理

是lock的一个实现类 主要依赖内部类Sync实现加锁释放锁 Sync 继承AbstractQueuedSynchronizer 锁状态是通过state属性进行标记 0 无锁 大于0 线程获取锁的次数 重入累加值 FairSync NonFairSync 公平和非公平实现 公平锁 线程阻塞 将线程插入队列尾部 依次执行 非公平锁 线程过来就尝试获得锁 不会去唤醒阻塞队列的线程

26、java并发包concurrent及常用的类

Java并发包基础元件:sun.misc.Unsafe(唯一一个 不在该包下的)、LockSupport Java并发包核心框架:AbstractQueuedSynchronizer、Condition接口、Lock接口 Java并发包同步组件:ReentrantLock、CylicBarrer、Semaphore、CountDownLatch、ReadWriteLock/ReentrantReadWriteLock、StampedLock 同步数据结构之原子类:AtomicInteger、AtomicBoolean、AtomicLong、AtomicReference等 同步工具类:Executor、Exchanger、CountDownLatch、CyclicBarrier、Semaphore 同步数据结构之Queue类:BlockingQueue接口、ArrayBlockingQueue、LinkedBlockingQueue、ConcurrentLinkedQueue、LinkedTransferQueue、PriorityBlockingQueue、DelayQueue、SynchronousQueue 同步数据结构之Deque类:BlockingDeque接口、ConcurrentLinkedDeque、LinkedBlockingDeque 同步数据结构之List类:CopyOnWriteArrayList 同步数据结构之Set类:CopyOnWriteArraySet、ConcurrentSkipListSet 同步数据结构之Map类:ConcurrentMap接口、ConcurrentHashMap、ConcurrentNavigableMap、ConcurrentSkipListMap 线程池:Executor、ExecutorService、ExecutorCompletionService、Executors、ScheduledThreadPoolExecutor fork/join:ForkJoinPool、ForkJoinTask、ForkJoinWorkerThread等。

27、FutureTask是什么?

FutureTask可用于异步获取执行结果或取消执行任务的场景。 执行多任务计算 在高并发环境下确保任务只执行一次

28、一个线程如果出现了运行时异常会怎么样

中断执行

29、生产者消费者模型的作用是什么

松耦合

30、ReadWriteLock是什么

减少锁竞争 提升性能 如三个线程同时读取一个变量 如果是可重入锁 那就是串行执行 如果使用读写锁就是并行执行

31、Java中用到的线程调度算法是什么?

抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行

32、线程池中的阻塞队列如果满了怎么办?

先看有没有达到最大线程数 达到了就走拒绝策略 没有达到就新建线程执行

33、线程池中 submit()和 execute()方法有什么区别?

submit可以获取返回值 execute无法获取返回值

34、介绍一下 AtomicInteger 类的原理?

基于CAS提供原子性的操作 volatile修饰变量value 保证可见性
35、多线程锁的升级原理是什么? 无锁 偏向锁 轻量级锁 重量级锁

36、Java 内存模型 happens-before原则

程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。 volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。 start()规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。 join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。 程序中断规则:对线程interrupted()方法的调用先行于被中断线程的代码检测到中断时间的发生。 对象finalize规则:一个对象的初始化完成(构造函数执行结束)先行于发生它的finalize()方法的开始。

37、公平锁/非公平锁

插队与不可插队 插队不一定成功

38、可重入锁

reentrantlock与synchronized 避免死锁 已经拥有锁之后不需要释放仍然可以进入同步代码

39、独享锁、共享锁

一次只能被一个线程所持有 写锁 synchronized reentrantlock 一次可以被多个线程同时持有 读锁 readlock

40、偏向锁/轻量级锁/重量级锁

偏向锁、轻量级锁和重量级锁是Java中用于实现同步的不同锁机制。它们的设计目标是为了在不同场景下提供最佳的性能和吞吐量。
  1. 偏向锁(Biased Locking):
      • 偏向锁是为了优化无竞争场景下的锁操作而引入的。它的设计思想是,在多线程环境中,锁往往只有一个线程持有。因此,当一个线程第一次获得锁时,JVM会将对象头中的标记设置为偏向锁,并将线程ID记录在对象头中。
      • 在后续的锁操作中,如果没有竞争,持有偏向锁的线程可以直接进入临界区,无需进行额外的同步操作,提高了性能。
      • 如果其他线程尝试获取偏向锁,JVM会检查偏向锁的线程ID是否是当前线程,如果是,则表示没有竞争,可以直接进入临界区。否则,偏向锁会升级为轻量级锁。
  1. 轻量级锁(Lightweight Locking):
      • 轻量级锁是为了解决短期竞争场景下的锁操作而引入的。它使用了CAS(Compare and Swap)操作来避免使用互斥量。
      • 当一个线程尝试获取锁时,JVM会使用CAS操作尝试将对象头中的标记更新为锁记录(Lock Record),如果成功,表示获取锁成功,进入临界区。
      • 如果有其他线程竞争同一个锁,JVM会使用自旋来等待锁的释放。如果自旋超过一定次数或者持有锁的线程被阻塞,轻量级锁会升级为重量级锁。
  1. 重量级锁(Heavyweight Locking):
      • 重量级锁是传统的同步机制,采用互斥量(Mutex)实现。当多个线程竞争同一个锁时,其他线程会被阻塞,等待锁的释放。
      • 重量级锁通过操作系统的线程阻塞原语(如操作系统提供的互斥量)来实现线程的阻塞和唤醒。这涉及到用户态和内核态之间的切换,开销较大。
      • 重量级锁适用于长期竞争场景,其中多个线程频繁地竞争同一个锁。
需要注意的是,锁的升级和降级是根据竞争情况动态进行的,即根据实际运行时的线程竞争情况来选择适当的锁机制。JVM会根据锁的竞争情况自动选择偏向锁、轻量级锁或重量级锁,以提供最佳的性能和吞吐量。
在Java中,锁的选择是由JVM自动进行的,开发人员通常不需要显式地选择锁的类型。然而,了解不同类型的锁机制可以帮助我们理解Java同步的内部工作原理,以及如何编写高效的并发代码。

41、如何保证内存可见性

volatile 内存屏障来保证 在每一个写操作之后会加入store内存屏障命令,将变量的最新值保存到主内存 在每一个读操作之前会加入load内存屏障命令,强制工作内存从主内存读取该变量的最新值

42、非核心线程延迟死亡,如何实现?

设置空闲时间大小

43、ConcurrentHashMap读操作为什么不需要加锁?

Node的成员变量是用volatile修饰的

44、ThreadLocal 如何解决 Hash 冲突?

链表法 还有一种解决办法是开放地址法 继续查找下一个空的槽位

45、ThreadLocal 的内存泄露是怎么回事?

底层是存放到threadlocalmap, key是弱引用,gc回收的时候没有把key删除,value还存在,所以在使用完之后remove,将key从map中删除

46、为什么ThreadLocalMap 的 key是弱引用,设计理念是?

gc回收的时候就回收掉

47、同步方法和同步代码块的区别是什么?

同步方法作用的是调用这个方法的对象,不同对象调用不会阻塞,同一对象调用会阻塞 如果是静态方法则无论是不是同一个对象都会阻塞 同步代码块需要看锁的类型

48、在Java中Lock接口比synchronized块的优势是什么?如果你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?

synchronized 自动释放锁 关键字 由jvm执行 悲观锁 lock 手动释放锁 接口 代码控制 线程竞争激烈性能较好 乐观锁 读锁和写锁

49、用Java实现阻塞队列。

public class BlockQueue { // push的锁 private final static Object pushLock = new Object(); // pop的锁 private final static Object popLock = new Object(); // 存储数据 private Stack stack; public BlockQueue() { stack = new Stack<>(); } public synchronized void push(T t) { int MAX_SIZE = 3; if (stack.size() >= MAX_SIZE) { // 超过了最大长度,那么就等待 pushLock(); } stack.push(t); // 解开pop的锁 popUnlock(); } public T pop() { if (stack.size() == 0) { // 不能pop,那么就等待 popLock(); } T t = stack.pop(); // 解开push的锁 pushUnlock(); return t; } // push锁 private void pushLock() { synchronized (pushLock) { try { pushLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } // 解开push锁 private void pushUnlock() { synchronized (pushLock) { pushLock.notify(); } } // pop锁 private void popLock() { synchronized (popLock) { try { popLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } // 解开pop锁 private void popUnlock() { synchronized (popLock) { popLock.notify(); } } }

50、什么是竞争条件?你怎样发现和解决竞争?

两个线程在一个没有同步的对象上交替执行会产生竞争条件 看是不是原子操作 锁解决竞争

51、为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?

run只是一个普通的方法 而start调用之后会交给cpu调度 实现多线程并发执行

52、Java中你怎样唤醒一个阻塞的线程?

如果是wait阻塞调用notify notify all唤醒 如果是condition的await阻塞,调用signal signallall 如果是locksupport的park阻塞 调用unpark唤醒

53、什么是不可变对象,它对写并发应用有什么帮助?

初始化之后不允许修改 线程安全

54、你在多线程环境中遇到的共同的问题是什么?你是怎么解决它的?

同时执行十个线程 有任务执行完了优先获取执行结果进行处理 competion service

55、Java 中能创建 volatile数组吗

可以创建 但是只能保证引用不可变 里面的元素还是可以变化的

56、volatile 能使得一个非原子操作变成原子操作吗

不保证原子性

57、我们能创建一个包含可变对象的不可变对象吗?

可以,比如日期类型的参数

58、在多线程环境下,SimpleDateFormat是线程安全的吗

不是 有一个成员变量calendar 多个线程会修改这个变量

59、为什么Java中 wait 方法需要在 synchronized 的方法中调用?

因为wait会释放锁

60、BlockingQueue,CountDownLatch及Semeaphore的使用场景

semeaphore 控制线程的并发数

61、Java中interrupted 和 isInterruptedd方法的区别?

isInterripted仅仅是查询当前线程的中断状态 interrupted查询之后会清除原状态 interrupt 方法中断线程 修改线程状态 抛出异常InterruptedException

62、怎么检测一个线程是否持有对象监视器

Thread.holdsLock(Object obj)方法

63、什么情况会导致线程阻塞

sleep wait 等待获取🔒 Io时间比较长 进入阻塞

64、Thread.sleep(1000)的作用是什么?

睡眠一秒 释放cpu的执行权

65、使用多线程可能带来什么问题

执行顺序错乱 共享数据修改导致的脏读问题

66、说说线程的生命周期和状态?

新建 就绪 运行 阻塞 死亡

67、什么是上下文切换

线程上下文切换 进程上下文切换 cpu轮训调度线程

68、Java Monitor 的工作机理

临界区 互斥锁 对象头

69、线程池都有哪几种工作队列

  • ArrayBlockingQueue 是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
  • LinkedBlockingQueue 一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
  • SynchronousQueue 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool(5)使用了这个队列。
  • PriorityBlockingQueue 一个具有优先级的无限阻塞队列。

70、说说几种常见的线程池及使用场景?

new fixedThreadPool newCachedThreadPool new scheduledThreadPool newSingletonThreadPool

71、使用无界队列的线程池会导致内存飙升吗?

会,如果线程执行时间过长,任务无限增加直到oom

72、为什么阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建?

有缺陷 比如new cachedthreadpoolexecutor 无界队列 容易导致oom

73、Future有缺陷嘛?

调用get是阻塞的
 
PS:
这篇文章分享了100多道面试题,挺不错的
介绍一个给图片添加水印的免费网站Git
Loading...