回答重点在 Elasticsearch 集群中,Master 主节点的选举是通过一种基于 Zen Discovery 模块的选举算法实现的。这个流程主要包含以下几个步骤:
1)集群形成:所有节点启动后,会尝试发现并加入集群。每个节点都会向其他节点发送探测信息来确认集群成员。2)数据同步:在确认成员后,各个节点之间会进行数据同步,确保大家都了解到彼此的状态。3)主节点提名:各节点提名自己,选择...
回答重点可以采取以下措施:
1)确保集群中有一个明确的主节点(Master)选举机制。
2)正确配置 minimum_master_nodes 参数。据官方建议,这个参数的值应该设置为 (N/2) + 1,其中 N 是集群中的主节点数(Master-eligible,可以认为是有资格当选主节点的节点)。这可以防止发生脑裂(split-brain)问题,从而避免双 Master 的情况。
3)...
回答重点1)使用 Executors 工厂类,例如Executors.newFixedThreadPool(10);
2)使用 ThreadPoolExecutor 直接创建线程池
1234567ExecutorService threadPool = new ThreadPoolExecutor( 5, // corePoolSize 10, // maximumPoolSiz...
回答重点
类名
描述
线程安全模型
使用场景
Vector
线程安全的动态数组
每个方法加锁
较低并发需求的场景(不推荐使用)
Hashtable
线程安全的哈希表
每个方法加锁
较低并发需求的场景(不推荐使用)
ConcurrentHashMap
线程安全的哈希表(高并发)
分段锁,支持高并发
高并发的场景,如缓存、分布式锁等
CopyOnWriteArrayList...
回答重点线程安全是指多个线程访问某一共享资源时,能够保证一致性和正确性,即无论线程如何交替执行,程序都能够产生预期的结果,且不会出现数据竞争或内存冲突。在 Java 中,线程安全的实现通常依赖于同步机制和线程隔离技术。
扩展知识常用的线程安全措施
同步锁:通过 synchronized 关键字或 ReentrantLock 实现对共享资源的同步控制。
原子操作类:Java 提供的 Atomi...
回答重点在 Java 中,线程之间的通信是指多个线程协同工作,主要实现方式包括:
1)共享变量:
线程可以通过访问共享内存变量来交换信息(需要注意同步问题,防止数据竞争和不一致)。
共享的也可以是文件,例如写入同一个文件来进行通信。
2)同步机制:
synchronized:Java 中的同步关键字,用于确保同一时刻只有一个线程可以访问共享资源,利用 Object 类提供的 wait(...
回答重点DelayQueue 是一个阻塞队列,而 ScheduledThreadPool 是线程池,不过内部核心原理都是差不多的。
DelayQueue 是利用优先队列存储元素,当从队列中获取任务的时候,如果最老的任务已经到了执行时间,可以从队列中出队一个任务,反之可以获得 null 或者阻塞等待任务到时。
ScheduledThreadPool 内部也使用的一个优先队列 DelayedWo...
回答重点Java 的 Timer 是一个用于调度任务的工具类,用于在未来某个时刻执行任务或周期性地执行任务。Timer 类一般与 TimerTask 搭配使用,其中 TimerTask 是一个需要执行的任务。
适用于简单的定时任务,如定时更新、定期发送报告等。
扩展知识Timer 使用TimerTask 是 Timer 需要执行的任务,它是一个实现了 Runnable 接口的抽象类,必须通过...
回答重点时间轮(Time Wheel) 是一种用于管理和调度大量定时任务的数据结构。它是一种高效的定时任务调度算法,主要用于优化任务调度的效率,特别是在需要处理大量定时任务时。
时间轮是一种环形的数据结构,通过将时间划分为若干个时间片(槽),每个时间片负责管理一定时间段(如秒、分钟等内的任务。
工作原理:
时间轮的中心是一个环形结构,每个槽表示一个时间段。当时间轮的指针移动到某个槽时,该槽...
回答重点Semaphore (信号量)是 Java 并发包中的一个同步工具类,用于管理一组许可(permits)。每个许可可以被一个线程持有,当许可被持有时,其他线程需要等待才能获取许可。即可以限制同时访问特定资源的线程数量,确保在特定时刻只有有限数量的线程能够访问资源。
扩展知识基本概念:
许可(Permits):表示可以访问资源的线程数量。Semaphore 对象内部维护了一个许可计数器...
回答重点CyclicBarrier 是一个同步辅助类,允许一组线程在执行某个任务时相互等待,直到所有线程都到达屏障(barrier)之后才继续执行。它的设计目的是解决多线程并发任务中需要同步的场景,确保所有参与的线程都在特定的点上完成执行,然后一起继续后续的任务。
扩展知识基本概念
屏障(Barrier):一个线程在调用 await() 方法时会被阻塞,直到所有参与的线程都到达屏障点。屏障点...
回答重点CountDownLatch 是 JUC 中的一个同步辅助类,它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。CountDownLatch 通过一个计数器来实现,计数器的初始值由构造方法传入。每当一个线程完成工作后,计数器会递减。当计数器到达零时,所有等待的线程会被唤醒并继续执行。
主要功能:
等待事件完成:通过 await() 方法,线程可以等待其他线程完成某些操作...
回答重点StampedLock 是 Java 8 引入的一个锁机制,与传统的 ReentrantLock 和 ReadWriteLock 相比,StampedLock 通过引入乐观读锁和时间戳(stamp)的概念,提升了读写性能,尤其是在读多写少的场景下。
核心特性:
写锁(write lock):独占模式的锁,和 ReentrantLock 类似,保证写操作的排他性。
悲观读锁(read...
回答重点CompletableFuture 是 Java 8 引入的一个强大的异步编程工具。允许非阻塞地处理异步任务,并且可以通过链式调用组合多个异步操作。
核心特性:
异步执行:使用 runAsync() 或 supplyAsync() 方法,可以非阻塞地执行任务。
任务的组合:可以使用 thenApply()、thenAccept() 等方法在任务完成后进行后续操作,支持链式调用。
异...
重点Java的ForkJoinPool 是Java 7引入的一个专门用于并行执行任务的线程池,它采用“分而治之”(divide and conquer)算法来解决大规模的并行问题。
核心机制:
Fork(分解):任务被递归分解为更小的子任务,直到达到不可再分的程度。
Join(合并):子任务执行完毕后,将结果合并,形成最终的解决方案。
工作窃取算法:ForkJoinPool使用了一种称为...
回答重点阻塞队列主要用来阻塞队列的插入和获取操作,当队列满了的时候插入操作会被阻塞,直到队列有空位。当队列为空的时候获取操作会被阻塞,直到队列有值。
常用在实现生产者和消费者场景,在笔试题中比较常见。
常见的阻塞队列包括:
ArrayBlockingQueue:一个有界队列,底层基于数组实现。需要在初始化时指定队列的大小,队列满时,生产者会被阻塞,队列空时,消费者会被阻塞。
Linked...
回答重点Java 中的原子类是通过使用硬件提供的原子操作指令(如 CAS,Compare-And-Swap)来确保操作的原子性,从而避免线程竞争问题。
常用的原子类有以下几种:
AtomicInteger:用于操作整数的原子类,提供了原子性的自增、自减、加法等操作。
AtomicLong:与 AtomicInteger 类似,但用于操作 long 型数据。
AtomicBoolean:用于...
回答重点在 Java 中累加器(Accumulator)一般指的是LongAdder 和 DoubleAdder 类,它们在高并发场景下比传统的 AtomicLong 更具优势。
LongAdder 和 DoubleAdder
LongAdder:适用于 long 类型的累加操作,提供了高效的累加功能,尤其是在多线程环境中。
DoubleAdder:适用于 double 类型的累加操作,同样...
回答重点轻量级锁 CAS 失败了之后,不会有自旋操作,会直接进入重量级锁膨胀过程。
扩展知识关于 Synchronized 专门翻阅 JVM HotSpot 1.8 的源码来研究了了,那时候就发现有一个点,一个几乎网上所有文章包括《Java并发编程的艺术》也是这样说的一个点。
锁升级想必网上有太多文章说过了,这里提到当轻量级锁 CAS 失败,则当前线程会尝试使用自旋来获取锁。
其实起初...
回答重点当重量级锁释放了之后,锁对象是无锁的。
有新的线程来竞争的话又会从轻量级锁开始。
扩展知识我们都知道 synchronized 的锁升级,也听过锁升级之后不会降级,所以理所当然的认为当一个锁升级为重量级锁之后,任何线程再来争抢之后会走重量级锁的逻辑。
不会再从无锁到偏向锁到轻量级锁再到重量级锁。
那事实是怎样的呢?这篇咱们不看源码,直接整结果。
我们直接看锁对象的锁标志来判断。
...
回答重点锁自适应自旋 是 Java 锁优化中的一种机制,用于减少线程在竞争锁时频繁挂起和恢复的开销。
自适应自旋的核心思想是,在锁争用较少的情况下,线程在进入等待状态前,先执行一段自旋操作(即短暂忙等),而不是立刻挂起线程。
在 Java 中 Syncronized 在争抢重量级锁时候会自旋。具体指的是在重量级锁时,一个线程如果竞争锁失败会进行自旋操作,说白了就是执行一些无意义的执行,空转 ...
回答重点主要有以下两种常见的优化方法:
1)减小锁的粒度(使用的时间):
尽量缩小加锁的范围,减少锁的持有时间。即在必要的最小代码块内使用锁,避免对整个方法或过多代码块加锁。
使用更细粒度的锁,比如将一个大对象锁拆分为多个小对象锁,以提高并行度(参考 HashTable 和ConcurrentHashMap 的区别)。
对于读多写少的场景,可以使用读写锁(ReentrantReadWrit...
回答重点读写锁,它允许多个线程同时读取共享资源,而在写操作时确保只有一个线程能够进行写操作(读读操作不互斥,读写互斥、写写互斥)。这种机制适合于读多写少的场景,因为它提高了系统的并发性和性能。
Java 中的 ReadWriteLock 是通过 ReentrantReadWriteLock 实现的,它提供了以下两种锁模式:
读锁(共享锁):允许多个线程同时获取读锁,只要没有任何线程持有写锁...
回答重点Java 内存模型(Java Memory Model, JMM) 是 Java 虚拟机 (JVM) 定义的一种规范,用于描述多线程程序中变量(包括实例字段、静态字段和数组元素)如何在内存中存储和传递的规则。规范了线程何时会从主内存中读取数据、何时会把数据写回主内存。
JMM 的核心目标是确保多线程环境下的可见性、有序性和原子性,从而避免由于硬件和编译器优化带来的不一致问题。
可见...
回答重点happens-before 规则是 Java 内存模型 (Java Memory Model, JMM) 中的核心概念,用于定义多线程程序中操作的可见性和顺序性。它通过指定一系列操作之间的顺序关系,确保线程间的操作是有序的,避免由于重排序或线程间数据不可见导致的并发问题。
happens-before 规则的主要规则:1)程序次序规则:在一个线程中,代码的执行顺序是按照程序中的书写...
回答重点指令重排是 Java 编译器和处理器为了优化性能,在保证单线程程序语义不变的情况下,对指令执行顺序进行调整的过程。在多线程环境下,指令重排可能导致线程之间的操作出现不同步或不可见的现象,因此 Java 提供了内存模型(JMM)和相关机制(如 volatile 和 synchronized)来限制这种行为,确保并发操作的正确性。
主要原因:
编译器优化:编译器会在不影响单线程程序语义的...
回答重点不可以。
你可能看到一些答案说可以保证可见性,那不是我们常说的可见性。
一般而言我们指的可见性是一个线程修改了共享变量,另一个线程可以立马得知更改,得到最新修改后的值。
而 final 并不能保证这种情况的发生,volatile 才可以。
而有些答案提到的 final 可以保证可见性,其实指的是 final 修饰的字段在构造方法初始化完成,并且期间没有把 this 传递出去,那么当...
回答重点因为在多线程编程中,多个线程可能会同时访问和修改共享变量,导致线程安全问题。ThreadLocal 提供了一种简单的解决方案,使每个线程都有自己的独立变量副本,避免了多线程间的变量共享和竞争,从而解决了线程安全问题。
与通过加锁、同步块等传统方式来保证线程安全相比。ThreadLocal 不需要对变量访问进行同步,减少了上下文切换、锁竞争的性能损耗。
扩展知识常见应用场景
数据库连接...
回答重点ThreadLocal 提供了一种线程内独享的变量机制,使每个线程都能有自己独立的变量副本。每个线程内部维护一个 ThreadLocalMap,这个 ThreadLocalMap 用于存储线程独立的变量副本。ThreadLocalMap 以 ThreadLocal 实例作为键,以线程独立的变量副本作为值。不同线程通过 ThreadLocal 获取各自的变量副本,而不会影响其他线程的数...
回答重点使用弱引用作为 ThreadLocal 的键可以防止内存泄漏。若 ThreadLocal 实例被不再需要的线程持有为强引用,那么当该线程结束时,相关的 ThreadLocal 实例及其对应的数据可能无法被回收,导致内存持续占用。
而弱引用允许垃圾回收器在内存不足时回收对象。这样,当没有其他强引用指向某个 ThreadLocal 实例时,它可以被及时回收,避免长时间占用内存。
扩展知识...