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

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

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

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

5.守卫块

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

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

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

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

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

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

70

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

public Integer 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");
            throw new IllegalStateException();
        }
    }
    return retVal;
}

尽管上面的代码将队列用作对象监视器,但是在多线程环境中它无法正确运行。原因是它有两个单独的同步块。当第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,以便每种方法均可访问它。另一个用例是传输事务信息或通常在所有方法中都应可访问的对象,而无需在方法之间传递它们。

目录
相关文章
|
6天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
12天前
|
存储 缓存 Oracle
Java I/O流面试之道
NIO的出现在于提高IO的速度,它相比传统的输入/输出流速度更快。NIO通过管道Channel和缓冲器Buffer来处理数据,可以把管道当成一个矿藏,缓冲器就是矿藏里的卡车。程序通过管道里的缓冲器进行数据交互,而不直接处理数据。程序要么从缓冲器获取数据,要么输入数据到缓冲器。
Java I/O流面试之道
|
1天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
10 2
|
8天前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
28 4
|
9天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
42 4
|
10天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
1月前
|
存储 安全 算法
Java面试题之Java集合面试题 50道(带答案)
这篇文章提供了50道Java集合框架的面试题及其答案,涵盖了集合的基础知识、底层数据结构、不同集合类的特点和用法,以及一些高级主题如并发集合的使用。
81 1
Java面试题之Java集合面试题 50道(带答案)
|
15天前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
33 2
|
21天前
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
45 5
|
20天前
|
存储 Java
[Java]面试官:你对异常处理了解多少,例如,finally中可以有return吗?
本文介绍了Java中`try...catch...finally`语句的使用细节及返回值问题,并探讨了JDK1.7引入的`try...with...resources`新特性,强调了异常处理机制及资源自动关闭的优势。
18 1