面试 Java 并发编程八股文十问十答第十期
面试 Java 并发编程八股文十问十答第十期
作者:程序员小白条,个人博客
相信看了本文后,对你的面试是有一定帮助的!关注专栏后就能收到持续更新!
⭐点赞⭐收藏⭐不迷路!⭐
1)Executors和ThreaPoolExecutor创建线程池的区别
- Executors是一个工厂类,提供了一些静态方法用于创建不同类型的线程池。它隐藏了ThreadPoolExecutor的一些细节,简化了线程池的创建过程。但是,它的一些默认配置可能不适合所有场景,因此使用时需要注意。
- ThreadPoolExecutor是线程池的实现类,提供了更灵活的配置选项。可以通过构造函数来指定核心线程数、最大线程数、线程空闲时间、任务队列等参数,以及自定义拒绝策略。可以根据具体需求进行定制化配置。
2)你知道怎么创建线程池吗?
可以使用ThreadPoolExecutor类的构造函数来创建线程池,也可以使用Executors类提供的静态方法来创建线程池。以下是两种常见的创建线程池的方法:
- 使用ThreadPoolExecutor构造函数:
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit, BlockingQueue);
其中,corePoolSize表示核心线程数,maximumPoolSize表示最大线程数,keepAliveTime表示线程空闲时间,TimeUnit表示时间单位,BlockingQueue表示任务队列。
- 使用Executors类的静态方法:
ExecutorService executor = Executors.newFixedThreadPool(nThreads);
其中,nThreads表示线程池中的线程数量。
3)ThreadPoolExecutor构造函数重要参数分析
- corePoolSize:核心线程数,表示线程池中保持活动状态的线程数量,即使它们处于空闲状态也不会被回收。
- maximumPoolSize:最大线程数,表示线程池中允许存在的最大线程数量。
- keepAliveTime:线程空闲时间,表示当线程池中的线程数量超过核心线程数时,空闲线程在被回收之前等待新任务的时间。
- TimeUnit:时间单位,用于指定keepAliveTime的单位,如秒、毫秒等。
- BlockingQueue:任务队列,用于保存等待执行的任务。可以选择不同类型的队列,如ArrayBlockingQueue、LinkedBlockingQueue等,以满足不同的需求。
- ThreadFactory:线程工厂,用于创建新的线程。可以自定义线程工厂来指定线程的名称、优先级等属性。
- RejectedExecutionHandler:拒绝策略,用于处理无法接受的新任务。可以选择不同的拒绝策略,如AbortPolicy、CallerRunsPolicy等,或自定义拒绝策略来处理任务被拒绝的情况。
这些参数可以根据具体需求来进行配置,以创建适合的线程池。
4)一个简单的线程池Demo:Runnable+ThreadPoolExecutor 线程池实现原理
下面是一个简单的线程池Demo,使用Runnable接口和ThreadPoolExecutor类来实现线程池:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建一个固定大小为5的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交10个任务给线程池执行
for (int i = 0; i < 10; i++) {
Runnable task = new Task(i);
executor.execute(task);
}
// 关闭线程池
executor.shutdown();
}
static class Task implements Runnable {
private int taskId;
public Task(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
System.out.println("Task " + taskId + " is running.");
}
}
}
这个示例中,通过Executors类的newFixedThreadPool方法创建一个固定大小为5的线程池。然后,使用execute方法提交10个任务给线程池执行。每个任务都是一个实现了Runnable接口的Task类的实例。最后,调用shutdown方法关闭线程池。
线程池的实现原理是,线程池内部维护了一个线程队列和一个任务队列。当有任务提交时,线程池会从线程队列中取出一个空闲线程来执行任务。如果线程队列中没有空闲线程,且当前线程数小于最大线程数,则会创建新的线程。如果线程数已达到最大线程数,任务会被放入任务队列中等待执行。当线程执行完任务后,会继续从任务队列中取出下一个任务执行,直到任务队列为空。
通过使用线程池,可以更好地管理和控制线程的数量,避免线程创建和销毁的开销,提高系统的性能和资源利用率。
5)什么是原子操作?在 Java Concurrency API 中有哪些原子类 (atomic classes)?
原子操作是指不可被中断的操作,要么全部执行成功,要么全部不执行。在并发编程中,原子操作是保证多线程安全的重要手段。
Java Concurrency API中提供了一些原子类,用于支持原子操作。常见的原子类有:
- AtomicInteger:提供原子操作的int类型变量。
- AtomicLong:提供原子操作的long类型变量。
- AtomicBoolean:提供原子操作的boolean类型变量。
- AtomicReference:提供原子操作的引用类型变量。
- AtomicStampedReference:提供带有版本号的原子引用操作。
这些原子类提供了一些常用的原子操作方法,如get、set、compareAndSet等,保证了对变量的操作是原子的,避免了多线程并发访问时的数据不一致和竞态条件问题。
6)说一下 atomic 的原理?
atomic的原理是通过使用底层的硬件指令或锁机制来保证操作的原子性。
在硬件层面,现代处理器提供了一些原子操作的指令,如compare-and-swap (CAS)。CAS指令可以在一个原子操作中读取和修改一个内存位置的值,如果内存位置的值与预期值相等,则将新值写入该位置,否则不进行任何操作。CAS操作是原子的,因为它在执行期间会锁定内存位置,防止其他线程修改该值。
在Java中,原子类使用了CAS操作来保证原子性。它们使用了volatile变量和Unsafe类来实现底层的CAS操作。volatile变量保证了变量的可见性,而Unsafe类提供了直接操作内存的方法。原子类通过使用CAS操作来保证对变量的操作是原子的,从而避免了多线程并发访问时的数据不一致和竞态条件问题。
总结起来,atomic的原理是通过底层的硬件指令或锁机制来保证操作的原子性,从而实现多线程并发访问时的数据一致性和线程安全性。
7)在 Java 中 CycliBarriar 和 CountdownLatch 有什么区别?
- CountDownLatch是一种同步工具,它可以让一个或多个线程等待其他线程完成操作后再继续执行。它通过一个计数器来实现,计数器的初始值为线程的数量,每个线程完成操作后会将计数器减1,当计数器减到0时,等待的线程就会被唤醒继续执行。CountDownLatch只能使用一次,计数器的值无法重置。
- CyclicBarrier也是一种同步工具,它可以让一组线程互相等待,直到所有线程都达到某个共同的屏障点后再继续执行。CyclicBarrier的计数器可以重复使用,当所有线程都到达屏障点后,计数器会重置为初始值,等待的线程会被唤醒继续执行。
总的来说,CountDownLatch用于等待其他线程完成操作,而CyclicBarrier用于等待一组线程互相达到屏障点。CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以重复使用。
8)Semaphore 有什么作用
Semaphore是一种计数信号量,用于控制同时访问某个资源的线程数量。Semaphore维护了一个计数器,线程可以通过acquire方法获取许可证,表示线程占用了一个资源,计数器会减1;线程可以通过release方法释放许可证,表示线程释放了一个资源,计数器会加1。当计数器为0时,acquire方法会阻塞线程,直到有其他线程释放许可证。
Semaphore的主要作用是限制同时访问某个资源的线程数量,可以用于控制并发线程的数量,避免资源的过度竞争。它可以用于实现一些常见的并发模式,如连接池、限流器等。
9)什么是线程间交换数据的工具Exchanger
Exchanger是一种线程间交换数据的工具,它提供了一个同步点,两个线程可以在这个同步点交换数据。当一个线程调用Exchanger的exchange方法时,它会被阻塞,直到另一个线程也调用了exchange方法,然后两个线程会交换数据并继续执行。
Exchanger可以用于解决生产者-消费者模式中的数据交换问题,它可以让生产者线程和消费者线程在同一个同步点上交换数据。通过Exchanger,生产者线程可以将数据传递给消费者线程,消费者线程可以将处理结果传递回生产者线程,从而实现线程间的数据交换。
10)常用的并发工具类有哪些?
在Java中,常用的并发工具类有:
- CountDownLatch:用于等待其他线程完成操作后再继续执行。
- CyclicBarrier:用于等待一组线程互相达到屏障点后再继续执行。
- Semaphore:用于控制同时访问某个资源的线程数量。
- Exchanger:用于线程间交换数据。
- Lock和ReentrantLock:用于实现互斥锁。
- Condition:用于实现线程间的等待和通知机制。
- BlockingQueue:用于实现生产者-消费者模式的线程安全队列。
- ConcurrentHashMap:用于实现线程安全的哈希表。
- Atomic类:如AtomicInteger、AtomicLong等,提供原子操作的变量,用于实现线程安全的计数器、累加器等。
这些并发工具类提供了一些常见的并发操作的实现,可以帮助开发者更好地管理和控制多线程并发访问时的资源竞争和线程同步问题。
开源项目地址:https://gitee.com/falle22222n-leaves/vue_-book-manage-system
前后端总计已经 800+ Star,1.5W+ 访问!
⭐点赞⭐收藏⭐不迷路!⭐