收下这一波2021年,最新的,Java并发面试题(三)

简介: 收下这一波2021年,最新的,Java并发面试题

61.僵局情况有哪些要求?

通常,可以确定以下死锁要求:

  • 互斥:有一种资源在任何时间点只能由一个线程访问。
  • 资源持有:锁定一个资源后,线程尝试获取对某个其他排他资源的另一个锁定。
  • 无抢占:没有机制,如果一个线程在特定时间段内持有锁,则该机制可以释放资源。
  • 循环等待:在运行时发生一个星座,其中两个(或更多)线程分别在另一个线程上等待以释放已锁定的资源。

62.完全可以防止死锁吗?

为了防止死锁,必须消除一个或多个死锁的要求:

  • 互斥:在某些情况下,可以通过使用乐观锁定来防止互斥。
  • 资源持有:线程无法成功获取所有排他锁时,可能会释放其所有排他锁。
  • 无抢占:对独占锁使用超时将在给定时间后释放锁。
  • 循环等待:当所有线程以相同顺序获得所有排他锁时,不会发生循环等待。

63.是否可以实现死锁检测?

当监视所有排他锁并将其建模为有向图时,死锁检测系统可以搜索两个线程,每个线程都在等待另一个线程以释放已锁定的资源。然后可以通过某种异常强制等待线程释放另一个线程正在等待的锁。

4.2饥饿和活锁

64.什么是活锁?

活锁是这样的情况,其中两个或更多线程通过响应另一个线程引起的动作而相互阻塞。与死锁情况相反,死锁情况是两个或多个线程在一个特定状态下等待,而参与活动锁的线程则以防止其正常工作进展的方式更改其状态。一个示例是这样一种情况,其中两个线程尝试获取两个锁,但是在无法获取第二个锁时释放它们获取的锁。现在可能会发生,两个线程同时尝试获取第一个线程。由于只有一个线程成功,因此第二线程可以成功获取第二锁。现在,两个线程都持有两个不同的锁,但是由于两个线程都想要拥有两个锁,因此它们释放其锁并从头开始尝试。现在可能一次又一次地发生这种情况。

65.通过线程饥饿我们了解什么?

具有较低优先级的线程比具有较高优先级的线程获得的执行时间更少。当具有较低优先级的线程执行长时间的持久计算时,可能会发生这些线程没有足够的时间及时完成其计算的情况。它们似乎“饿死了”,因为具有更高优先级的线程会占用它们的计算时间。

66.同步块会导致线程饥饿吗?

没有定义线程可以进入同步块的顺序。因此,从理论上讲,如果许多线程正在等待同步块的入口,则某些线程必须比其他线程等待更长的时间。因此,他们没有足够的计算时间来及时完成工作。

5.守卫块

67.每个对象继承的哪两种方法java.lang.Object可用于实现简单的生产者/消费者方案?

当工作线程完成其当前任务并且新任务的队列为空时,它可以通过获取队列对象的内在锁并调用方法来释放处理器wait()。该生产者线程将唤醒该线程,该生产者线程将新任务放入队列中,并再次获取队列对象的相同内在锁并对其进行调用notify()

68.notify()和之间有什么区别notifyAll()

两种方法都用于唤醒一个或多个通过调用进入睡眠状态的线程wait()。虽然notify()仅唤醒其中一个等待线程,但notifyAll()唤醒所有等待线程。

69.如何确定通过调用唤醒哪个线程notify()

notify()如果有多个线程正在等待,则不指定将通过调用唤醒哪些线程。因此,代码不应依赖任何具体的JVM实现。

70下面的从某些队列实现中检索整数值的代码是否正确?


publicInteger getNextInt() {`` ``Integer retVal = ``null``;`` ``synchronized(queue) {`` ``try{`` ``while(queue.isEmpty()) {`` ``queue.wait();`` ``}`` ``} ``catch(InterruptedException e) {`` ``e.printStackTrace();`` ``}`` ``}`` ``synchronized(queue) {`` ``retVal = queue.poll();`` ``if(retVal == ``null``) {`` ``System.err.println(``"retVal is null"``);`` ``thrownewIllegalStateException();`` ``}`` ``}`` ``returnretVal;``}

尽管上面的代码将队列用作对象监视器,但是在多线程环境中它无法正确运行。原因是它有两个单独的同步块。当第6行中的另一个线程唤醒了两个线程时,两个线程在另一个notifyAll()同步块中一个接一个地输入。如果队列的第二个块现在只有一个新值,那么第二个线程将轮询一个空队列并获得null作为返回值。

6.不可变的对象

71.什么是不可变对象

  • 不变对象可以通过任何机制发布
  • 不可变对象可以被任何线程安全地使用,而无需额外的同步,即使不使用同步来发布它们。

72.如何创建一个不可变的对象

要创建一个不变的对象,您需要:

  • 不要添加任何设置方法
  • 声明所有字段的最终和私有
  • 如果字段是可变对象,则为获取方法创建其防御性副本
  • 如果必须将传递给构造函数的可变对象分配给字段,请为其创建防御性副本
  • 不允许子类覆盖方法。

73.为了实现不可变的类,您必须遵循哪些规则?

  • 所有字段均应为最终字段和私有字段。
  • 不应使用setter方法。
  • 应该将类本身声明为final,以防止子类违反不变性原则。
  • 如果字段不是原始类型,而是对另一个对象的引用:
  • 不应有将引用直接暴露给调用者的getter方法。
  • 不要更改引用的对象(或者至少更改这些引用对对象的客户端不可见)。

7,锁定对象

74.是否可以检查线程是否在某个给定对象上持有监视器锁定?

该类java.lang.Thread提供静态方法Thread.holdsLock(Object),当且仅当当前线程持有作为方法调用的参数给定的对象上的锁时,该方法才返回true。

75.通过锁争用我们了解什么?

当两个或多个线程在获取锁中竞争时,发生锁争用。调度程序必须决定是否让必须等待睡眠的线程并执行上下文切换以让另一个线程占用CPU,或者让等待线程忙于等待是否更有效率。两种方式都会将空闲时间引入劣质线程。

76.哪些技术有助于减少锁争用?

在某些情况下,可以通过应用以下技术之一来减少锁争用:

  • 锁的范围减小了。
  • 减少获取某个锁的次数(锁拆分)。
  • 使用硬件支持的乐观锁定操作而不是同步。
  • 尽可能避免同步。
  • 避免对象池。

77.可以将哪种减少锁争用的技术应用于以下代码?

synchronized` `(map) {``  ``UUID randomUUID = UUID.randomUUID();``  ``Integer value = Integer.valueOf(``42``);``  ``String key = randomUUID.toString();``  ``map.put(key, value);``}

上面的代码执行随机UUID的计算,并将文字42转换为同步块内的Integer对象,尽管这两行代码对于当前线程而言是本地的,并不影响其他线程。因此,可以将它们移出同步块:

UUID randomUUID = UUID.randomUUID();``Integer value = Integer.valueOf(``42``);``String key = randomUUID.toString();``synchronized` `(map) {``  ``map.put(key, value);``}

78.举例说明技术锁拆分。

当一个锁用于同步对同一应用程序不同方面的访问时,锁拆分可能是减少锁争用的一种方法。假设我们有一个实现对应用程序的某些统计数据进行计算的类。此类的第一个版本在每个方法签名中使用关键字sync,以便在多个并发线程损坏之前保护内部状态。这也意味着每个方法调用都可能导致锁争用,因为其他线程可能会尝试同时获取同一锁。但是对于每种方法中每种统计数据类型,可以将对象实例上的锁拆分为几个较小的锁。因此,试图递增统计数据D1的线程T1不必等待锁定,而线程T2同时更新数据D2。

79.通过锁条我们了解什么?

与锁拆分相反,在锁拆分中,我们针对应用程序的不同方面引入了不同的锁,而锁条使用了多个锁来保护同一数据结构的不同部分。这种技术的一个示例是ConcurrentHashMapJDKjava.util.concurrent软件包中的类。该Map实现使用内部不同的存储桶来存储其值。通过值的键选择存储桶。ConcurrentHashMap现在使用不同的锁来保护不同的哈希桶。因此,一个尝试访问第一个哈希存储桶的线程可以获取该存储桶的锁,而另一个线程可以同时访问第二个存储桶。与同步版本HashMap相比,当不同线程在不同存储桶上工作时,此技术可以提高性能。

80. Java Concurrency API中的Lock接口是什么?

java.util.concurrent.locks.Lock接口用作类似于同步块的线程同步机制。新的锁定机制比同步块更灵活,并提供更多选项。

锁和同步块之间的主要区别如下:

  • 保证顺序?同步块不提供对等待线程进行访问的顺序的任何保证。锁接口处理它。
  • 没有超时?如果未授予锁定,则同步块没有超时选项。锁定界面提供了这种选择。
  • 单一方法?同步块必须完全包含在一个方法中,而锁接口的方法lock()和unlock()可以在不同的方法中调用。

8,执行人

8.1执行器接口

81. ExecutorService在计时器上的优点

  • 与ExecutorService不同,Timer无法利用可用的CPU内核,特别是在使用ExecutorService之类的多个任务(例如ForkJoinPool)时
  • 如果您需要在多个任务之间进行协调,则ExecutorService提供了协作式API。假设您必须提交N个工作者任务,然后等待所有任务完成。您可以使用invokeAll API轻松实现它。如果要通过多个Timer任务实现相同的目标,这将不那么简单。
  • ThreadPoolExecutor提供了更好的API,用于管理线程生命周期。

82. Executor和ExecutorService这两个接口之间是什么关系?

该接口Executor仅定义一种方法:execute(Runnable)。该接口的实现将必须在将来的某个时间执行给定的Runnable实例。该ExecutorService接口是该接口的扩展,Executor并提供了其他方法来关闭基础实现,等待所有提交的任务终止,并且允许提交的实例Callable

83.当您submit()对队列已满的ExecutorService实例执行新任务时会发生什么?

如的方法签名submit()所示,该ExecutorService实现应该抛出RejectedExecutionException

84.什么是ScheduledExecutorService?

接口ScheduledExecutorService扩展了接口ExecutorService并添加了方法,该方法允许将新任务提交给应该在给定时间点执行的基础实现。有两种方法可以安排一次性任务,而有两种方法可以创建和执行定期任务。

8.2线程池

85.您知道一种简单的方法来构造具有5个线程的线程池,该线程池执行一些返回值的任务吗?

SDK提供了一个factory和utility类,Executors其静态方法newFixedThreadPool(int nThreads)允许创建具有固定线程数的线程池(MyCallable省略了的实现):

public` `static` `void` `main(String[] args) ``throws` `InterruptedException, ExecutionException {``  ``ExecutorService executorService = Executors.newFixedThreadPool(``5``);``  ``Future<Integer>[] futures = ``new` `Future[``5``];``  ``for` `(``int` `i = ``0``; i < futures.length; i++) {``    ``futures[i] = executorService.submit(``new` `MyCallable());``  ``}``  ``for` `(``int` `i = ``0``; i < futures.length; i++) {``    ``Integer retVal = futures[i].get();``    ``System.out.println(retVal);``  ``}``  ``executorService.shutdown();``}

86.是否可以在Java 8中使用线程池执行流操作?

集合提供了一种parallelStream()创建由线程池处理的流的方法。或者,您可以parallel()在给定流上调用中间方法,以将顺序流转换为并行对应流。

87.对象池是否始终可以提高多线程应用程序的性能?

试图通过池化来避免构建新对象的对象池可以提高单线程应用程序的性能,因为通过从池中请求新对象来交换对象创建成本。在多线程应用程序中,此类对象池必须具有对该池的同步访问权限,并且锁争用的额外成本可能会超过新对象的额外构造和垃圾回收所节省的成本。因此,对象池可能无法始终提高多线程应用程序的整体性能。

8.3前叉/连接

88.我们如何访问并行流操作所使用的线程池?

并行流操作所使用的线程池可以通过访问ForkJoinPool.commonPool()。这样,我们可以使用来查询其并行度commonPool.getParallelism()。水平不能在运行时改变,但它可以通过提供以下JVM参数来配置:-Djava.util.concurrent.ForkJoinPool.common.parallelism=5

89.使用Fork / Join框架可以解决哪些任务?

Fork / Join Framework的基类java.util.concurrent.ForkJoinPool基本上是一个线程池,它执行的实例java.util.concurrent.ForkJoinTask。该类ForkJoinTask提供了fork()和的两种方法join()。当fork()用于启动任务的异步执行时,该方法join()用于等待计算结果。因此,Fork / Join框架可用于实现分而治之的算法,其中将更复杂的问题分为多个更小且更容易解决的问题。

90.是否可以使用Fork / Join-Framework在数字数组中找到最小的数字?

通过使用分而治之算法可以解决在数字数组中找到最小数字的问题。可以轻松解决的最小问题是两个数字组成的数组,因为我们可以通过一个比较直接确定两个数字中较小的一个。使用分治法将初始数组分为相等长度的两个部分,并将这两个部分提供给RecursiveTask扩展该类的两个实例ForkJoinTask。通过分叉这两个任务,它们将被执行,或者直接解决问题(如果数组的切片长度为2),或者再次递归将数组分为两个部分,并派生两个新的RecursiveTasks。最后,每个任务实例返回其结果(通过直接计算结果或通过等待两个子任务)。然后,根任务返回数组中的最小数字。

91.这两个类RecursiveTask和和有RecursiveAction什么区别?

RecursiveTask方法相反compute()RecursiveAction不必返回值。因此,RecursiveAction当操作直接在某些数据结构上执行而不必返回计算值时,可以使用该操作。

9,并发集合

92.什么是并发收集类?

Java Collection类是快速失败的,这意味着如果在使用迭代器遍历某个线程的同时更改了Collection,则iterator.next()将抛出ConcurrentModificationException。

93.什么是Java内存模型?

Java内存模型描述了Java编程语言中的线程如何通过内存进行交互。连同代码的单线程执行描述一起,内存模型提供了Java编程语言的语义。

94.线程安全之间有什么区别HashMapHashtable特别是在线程安全方面有什么区别?

的方法Hashtable都是同步的。HashMap实施情况并非如此。因此Hashtable是线程安全的,而HashMap不是线程安全的。因此,对于单线程应用程序,使用“较新的”HashMap实现更为有效。

95.有一个简单的方法来创建一个任意执行的同步实例CollectionList还是Map

实用程序类Collections提供了方法synchronizedCollection(Collection)synchronizedList(List)并且synchronizedMap(Map)返回了由给定实例支持的线程安全的collection / list / map。

10,阻塞队列

96.什么是BlockingQueue?

BlockingQueue是一个Java队列,它支持以下操作:在检索和删除元素时等待队列变为非空,并在添加元素时等待队列中的空间变为可用。接口TransferQueue已添加。这是对BlockingQueue接口的改进,生产者可以在其中等待消费者接收元素。

11,原子变量

97.通过CAS操作我们了解什么?

CAS代表比较交换,并且意味着处理器提供单独的指令,仅在提供的值等于当前值时才更新寄存器的值。CAS操作可用于避免同步,因为线程可以通过向CAS操作提供其当前值和新值来尝试更新值。如果另一个线程同时更新了该值,则该线程的值不等于当前值,并且更新操作失败。然后,线程读取新值,然后重试。这样,必要的同步就可以通过乐观的旋转等待来交换。

98.哪些Java类使用CAS操作?

程序包中的SDK类java.util.concurrent.atomic类似于AtomicInteger或在AtomicBoolean内部使用CAS操作来实现并发增量。

public` `class` `CounterAtomic {``  ``private` `AtomicLong counter = ``new` `AtomicLong();``  ``public` `void` `increment() {``    ``counter.incrementAndGet();``  ``}``  ``public` `long` `get() {``    ``return` `counter.get();``  ``}``}

12.并发随机数

99.上课的目的是java.lang.ThreadLocal什么?

由于内存在不同线程之间共享,因此ThreadLocal提供了一种分别存储和检索每个线程的值的方法。ThreadLocal独立存储和检索每个线程的值的实现,这样,当线程A存储值A1且线程B将值B1存储在的同一实例中时ThreadLocal,线程A稍后从该ThreadLocal实例中检索值A1 ,线程B检索值B1。

100.有哪些可能的用例java.lang.ThreadLocal

的实例ThreadLocal可用于在整个应用程序中传输信息,而无需在方法之间传递此信息。示例是在实例中传输安全性/登录信息ThreadLocal,以便每种方法均可访问它。另一个用例是传输事务信息或通常在所有方法中都应可访问的对象,而无需在方法之间传递它们。


目录
相关文章
|
3月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
2月前
|
Java 大数据 Go
从混沌到秩序:Java共享内存模型如何通过显式约束驯服并发?
并发编程旨在混乱中建立秩序。本文对比Java共享内存模型与Golang消息传递模型,剖析显式同步与隐式因果的哲学差异,揭示happens-before等机制如何保障内存可见性与数据一致性,展现两大范式的深层分野。(238字)
83 4
|
6月前
|
缓存 Java 关系型数据库
2025 年最新华为 Java 面试题及答案,全方位打造面试宝典
Java面试高频考点与实践指南(150字摘要) 本文系统梳理了Java面试核心考点,包括Java基础(数据类型、面向对象特性、常用类使用)、并发编程(线程机制、锁原理、并发容器)、JVM(内存模型、GC算法、类加载机制)、Spring框架(IoC/AOP、Bean生命周期、事务管理)、数据库(MySQL引擎、事务隔离、索引优化)及分布式(CAP理论、ID生成、Redis缓存)。同时提供华为级实战代码,涵盖Spring Cloud Alibaba微服务、Sentinel限流、Seata分布式事务,以及完整的D
363 1
|
5月前
|
缓存 Java API
Java 面试实操指南与最新技术结合的实战攻略
本指南涵盖Java 17+新特性、Spring Boot 3微服务、响应式编程、容器化部署与数据缓存实操,结合代码案例解析高频面试技术点,助你掌握最新Java技术栈,提升实战能力,轻松应对Java中高级岗位面试。
477 0
|
2月前
|
缓存 安全 Java
如何理解Java中的并发?
Java并发指多任务交替执行,提升资源利用率与响应速度。通过线程实现,涉及线程安全、可见性、原子性等问题,需用synchronized、volatile、线程池及并发工具类解决,是高并发系统开发的关键基础。(238字)
227 4
|
5月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
365 83
|
5月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
342 83
|
5月前
|
Java 数据库连接 数据库
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
本文全面总结了Java核心知识点,涵盖基础语法、面向对象、集合框架、并发编程、网络编程及主流框架如Spring生态、MyBatis等,结合JVM原理与性能优化技巧,并通过一个学生信息管理系统的实战案例,帮助你快速掌握Java开发技能,适合Java学习与面试准备。
252 2
Java 相关知识点总结含基础语法进阶技巧及面试重点知识