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

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

1.进程和线程

1.我们对“并发”一词有什么了解?

并发是程序同时执行多个计算的能力。这可以通过将计算分布在计算机的可用CPU内核上,甚至在同一网络内的不同计算机上来实现。

2.进程和线程之间有什么区别?

进程是操作系统提供的执行环境,它具有自己的一组私有资源(例如,内存,打开的文件等)。与流程相反,线程位于流程内,并与流程的其他线程共享资源(内存,打开的文件等)。在不同线程之间共享资源的能力使线程更适合于对性能有重要要求的任务。

3.在Java中,什么是进程和线程?

在Java中,进程对应于正在运行的Java虚拟机(JVM),而线程位于JVM中,并且可以由Java应用程序在运行时动态创建和停止。

4.什么是调度程序?

调度程序是一种调度算法的实现,该算法管理进程和线程对某些有限资源(如处理器或某些I / O通道)的访问。大多数调度算法的目标是为可用的进程/线程提供某种负载平衡,以确保每个进程/线程都有适当的时间范围以排他地访问所请求的资源。

5. Java程序至少具有多少个线程?

每个Java程序都在主线程中执行;因此,每个Java应用程序都有至少一个线程。

6. Java应用程序如何访问当前线程?

可以通过调用currentThread()JDK类的静态方法来访问当前线程java.lang.Thread

12345678 publicclassMainThread {`` ``publicstaticvoidmain(String[] args) {`` ``longid = Thread.currentThread().getId();`` ``String name = Thread.currentThread().getName();`` ``...`` ``}``}

7.每个Java线程都有哪些属性?

每个Java线程都具有以下属性:

  • 在JVM中唯一的long类型的标识符
  • 类型为String的名称
  • int类型的优先级
  • 类型状态 java.lang.Thread.State
  • 线程所属的线程组

8.线程组的目的是什么?

每个线程都属于一组线程。JDK类java.lang.ThreadGroup提供了一些方法来处理整个线程组。使用这些方法,例如,我们可以中断组中的所有线程或设置其最大优先级。

9.线程可以具有哪些状态,每个状态的含义是什么?

  • NEW: 尚未启动的线程处于此状态。
  • RUNNABLE: 在Java虚拟机中执行的线程处于这种状态。
  • BLOCKED: 等待监视器锁定而被阻塞的线程处于此状态。
  • WAITING: 无限期地等待另一个线程执行特定操作的线程处于此状态。
  • TIMED_WAITING: 正在等待另一个线程执行操作的线程最多达到指定的等待时间,该线程处于此状态。
  • TERMINATED: 退出的线程处于此状态。

10.多线程编程的好处是什么?

多线程代码可以在以下方面提供帮助:

  • 改善应用程序响应能力
  • 有效地使用多处理器
  • 改善计划结构
  • 使用更少的系统资源

11.在多线程环境中遇到哪些常见问题?

  • 僵局–两个线程A和B分别持有lock_A和lock_B。他们两个都希望访问资源R。为了安全地访问R,需要lock_A和lock_B。但是线程A需要lock_B,线程B需要lock_A。但是他们两个都不准备放弃他们持有的锁。因此,没有任何进展。陷入僵局!
  • 种族条件–考虑生产者-消费者的经典例子。如果您在添加或从队列中删除项目之前忘记锁定该怎么办?想象一下,两个线程A和B试图在不锁定的情况下添加项目。线程A访问队列的后面。然后,调度程序就有机会运行线程B,该线程B成功添加了项并更新了尾指针。现在,线程A读取的尾指针已过时,但它认为它是尾并添加了该项。因此,B添加的项目是LOST!数据结构已损坏!更糟糕的是,清理时还可能导致内存泄漏。
  • 数据竞争–想象应该设置的标志变量。假设您已锁定以避免比赛条件。现在,不同的线程想要设置不同的值。由于调度程序可以以任何方式调度线程执行,因此您最终不知道标志的值。
  • 饥饿–这是线程调度程序引起的问题。一些线程没有机会运行和完成,或者无法获得所需的锁,因为其他线程被赋予了更高的优先级。他们“饿死”了CPU周期或其他资源。
  • 优先级反转–想象两个线程A和B。A具有比B高的优先级,因此比B具有更多的CPU周期。但是在访问共享资源时,B持有A所需的锁并屈服。现在,如果没有锁,A无法做任何事情,并且浪费了大量CPU周期,因为B没有获得足够的周期,但是拥有了锁。

12.我们如何设置线程的优先级?

使用方法可以设置线程的优先级setPriority(int)。要将优先级设置为最大值,我们使用常量Thread.MAX_PRIORITY,将其设置为最小值,我们使用常量,Thread.MIN_PRIORITY因为这些值在不同的JVM实现之间可能会有所不同。

13.什么是多线程中的上下文切换?

纯粹的多任务处理不存在。同时执行两项具有挑战性的任务是不可能的。因此,当我们执行多任务时,我们真正要做的就是不断地从一项任务切换到另一项任务。这就是上下文切换。

该术语起源于计算机科学。但是,它同样适用于人类执行的心理任务。毕竟,人类的思想在许多方面都类似于CPU。

就像运行多线程进程的CPU在运行另一个线程时暂时搁置一个给定线程的执行一样,人类的大脑也搁置一个任务以将其重点转移到另一个任务上。

14. Java中的绿色线程和本地线程之间的区别?

  • 绿色线程是指Java虚拟机本身在一个操作系统进程中创建,管理和上下文切换所有Java线程的模型。没有使用操作系统线程库。
  • 本机线程是指Java虚拟机使用操作系统线程库(在UnixWare上名为libthread)创建和管理Java线程,并且每个Java线程都映射到一个线程库线程。

15.我们对种族条件一词有什么了解?

竞争条件描述的星座图,其中某些多线程实现的结果取决于参与线程的确切计时行为。在大多数情况下,具有这种行为是不希望的,因此,竞赛条件一词还意味着由于缺少线程同步而导致的错误会导致不同的结果。竞争条件的一个简单示例是两个并行线程对整数变量的递增。由于该操作由一个以上的单个原子操作组成,因此可能发生两个线程读取并增加相同值的情况。在此并发增量之后,整数变量的数量不会增加2,而只会增加1

16.将对象实例从一个线程传递到另一个线程时,您需要考虑什么?

在线程之间传递对象时,必须注意这些对象不能同时由两个线程操纵。一个示例是一个Map实现,其键/值对由两个并发线程修改。为了避免并发修改出现问题,可以将对象设计为不可变的。

17.是否可以通过使用多线程来提高应用程序的性能?列举一些例子。

如果我们有多个CPU内核可用,并且可以在可用的CPU内核上并行化计算,则可以通过多线程来提高应用程序的性能。一个示例是应该缩放存储在本地目录结构中的所有图像的应用程序。生产者/消费者实现可以使用一个线程扫描目录结构和执行实际缩放操作的一堆工作线程,而不是一个接一个地遍历所有图像。另一个示例是镜像某些网页的应用程序。生产者线程可以解析第一个HTML页面并将发现的链接发布到队列中,而不是先加载一个HTML页面。工作线程监视队列并加载解析器找到的网页。

18.我们对可扩展性一词有什么了解?

可伸缩性是指程序通过向其添加更多资源来提高性能的能力。

19.是否可以通过使用多个处理器来计算应用程序的理论最大速度?

通过为应用程序提供多个处理器,阿姆达尔定律提供了一个计算理论上最大速度的公式。理论上的加速由S(n) = 1 / (B + (1-B)/n)下式计算:其中n表示处理器数量和B无法并行执行的程序部分。当n收敛于无穷大时,项(1-B)/n收敛于零。因此,在这种特殊情况下,公式可以简化为1/B。正如我们所看到的,理论上最大加速比是必须依次执行的分数的倒数。这意味着该分数越低,可以实现越多的理论加速。

20.提供一个示例,说明为什么单线程应用程序的性能改进会导致多线程应用程序的性能下降。

此类优化的一个突出示例是将List元素数量保持为单独变量的实现。由于该size()操作不必遍历所有元素,而是可以直接返回当前数量的元素,因此可以提高单线程应用程序的性能。在多线程应用程序中,由于多个并发线程可能会将元素插入列表,因此必须通过锁来保护其他计数器。当列表的更新次数超过size()操作的调用次数时,此附加锁定可能会降低性能。

2.线程对象

2.1定义和启动线程

21.如何用Java创建线程?

基本上,有两种方法可以用Java创建线程。 第一个是编写一个扩展JDK类java.lang.Thread并调用其方法的类start()

public` `class` `MyThread ``extends` `Thread {``  ``public` `MyThread(String name) {``    ``super``(name);``  ``}``  ``@Override``  ``public` `void` `run() {``    ``System.out.println(``"Executing thread "``+Thread.currentThread().getName());``  ``}``  ``public` `static` `void` `main(String[] args) ``throws` `InterruptedException {``    ``MyThread myThread = ``new` `MyThread(``"myThread"``);``    ``myThread.start();``  ``}``}

第二种方法是实现接口java.lang.Runnable,并将此实现作为参数传递给的构造函数java.lang.Thread

public` `class` `MyRunnable ``implements` `Runnable {``  ``public` `void` `run() {``    ``System.out.println(``"Executing thread "``+Thread.currentThread().getName());``  ``}``  ``public` `static` `void` `main(String[] args) ``throws` `InterruptedException {``    ``Thread myThread = ``new` `Thread(``new` `MyRunnable(), ``"myRunnable"``);``    ``myThread.start();``  ``}``}

22.为什么不应该通过调用其方法来停止线程stop()

线程不应该被使用过时的方法来停止stop()java.lang.Thread,因为该方法的调用导致线程解锁已获取所有的显示器。如果受释放锁之一保护的任何对象处于不一致状态,则此状态对所有其他线程可见。当其他线程对此不一致的对象进行处理时,这可能导致任意行为。

23.是否可以启动一个线程两次?

否,在通过调用线程的start()方法启动线程之后,第二次调用start()将抛出IllegalThreadStateException

24.以下代码的输出是什么?


publicclassMultiThreading {`` ``privatestaticclassMyThread ``extendsThread {`` ``publicMyThread(String name) {`` ``super``(name);`` ``}`` ``@Override`` ``publicvoidrun() {`` ``System.out.println(Thread.currentThread().getName());`` ``}`` ``}`` ``publicstaticvoidmain(String[] args) {`` ``MyThread myThread = ``newMyThread(``"myThread"``);`` ``myThread.run();`` ``}``}

上面的代码产生输出“ main”而不是“ myThread”。从该main()方法的第二行可以看出,我们错误地调用run()而不是方法start()。因此,没有启动新线程,但是该方法run()在主线程中执行。

25.什么是守护线程?

守护程序线程是当JVM决定是否应该停止时,不评估其执行状态的线程。当所有用户线程(与守护程序线程相反)终止时,JVM停止。因此,一旦所有用户线程停止,守护程序线程就可以用于实现监视功能,例如,JVM停止了该线程:


publicclassExample {`` ``privatestaticclassMyDaemonThread ``extendsThread {`` ``publicMyDaemonThread() {`` ``setDaemon(``true``);`` ``}`` ``@Override`` ``publicvoidrun() {`` ``while(``true``) {`` ``try{`` ``Thread.sleep(``1``);`` ``} ``catch(InterruptedException e) {`` ``e.printStackTrace();`` ``}`` ``}`` ``}`` ``}`` ``publicstaticvoidmain(String[] args) ``throwsInterruptedException {`` ``Thread thread = ``newMyDaemonThread();`` ``thread.start();`` ``}``}

即使守护线程仍在其无尽的while循环中运行,以上示例应用程序也会终止。

26.什么是Java线程转储?

Java线程转储是一种找出JVM中每个线程在特定时间点正在做什么的方法。

如果您的Java应用程序有时在负载下运行时挂起,这将特别有用,因为对转储的分析将显示线程卡在哪里。

您可以在Unix / Linux下通过运行kill -QUIT 来生成线程转储,而在Windows下则可以通过按Ctl + Break来生成线程转储。

27.是否可以在启动普通用户线程后将其转换为守护线程?

用户线程一旦启动就无法转换为守护线程。thread.setDaemon(true)在已经运行的线程实例上调用该方法会导致IllegalThreadStateException

28.忙碌等待使我们了解什么?

繁忙等待是指通过执行一些活动计算来等待事件的实现,这些计算使线程/进程占用了处理器,尽管调度程序可以将其从处理器中删除。繁忙等待的一个示例是将等待时间花费在一个循环中,该循环一次又一次地确定当前时间,直到到达某个时间点为止:

Thread thread = ``new` `Thread(``new` `Runnable() {``  ``@Override``  ``public` `void` `run() {``    ``long` `millisToStop = System.currentTimeMillis() + ``5000``;``    ``long` `currentTimeMillis = System.currentTimeMillis();``    ``while` `(millisToStop > currentTimeMillis) {``      ``currentTimeMillis = System.currentTimeMillis();``    ``}``  ``}``});

29. Java中的wait()和sleep()方法之间有什么区别?

等待():

  • wait()方法释放锁定。
  • wait()是Object类的方法。
  • wait()是非静态方法–公共最终void wait()引发InterruptedException {//…}
  • 应该通过notify()或notifyAll()方法通知wait()。
  • 需要从循环中调用wait()方法以处理错误警报。
  • 必须从同步上下文(即同步方法或块)中调用wait()方法,否则它将引发IllegalMonitorStateException

sleep():

  • sleep()方法不会释放锁。
  • sleep()是java.lang.Thread类的方法。
  • sleep()是静态方法–公共静态无效睡眠(长毫秒,int nanos)抛出InterruptedException {//…}
  • 在指定的时间后,sleep()完成。
  • sleep()最好不要从循环中调用(即参见下面的代码)。
  • sleep()可以从任何地方调用。没有具体要求。



目录
相关文章
|
5月前
|
Java 大数据 Go
从混沌到秩序:Java共享内存模型如何通过显式约束驯服并发?
并发编程旨在混乱中建立秩序。本文对比Java共享内存模型与Golang消息传递模型,剖析显式同步与隐式因果的哲学差异,揭示happens-before等机制如何保障内存可见性与数据一致性,展现两大范式的深层分野。(238字)
153 4
|
5月前
|
缓存 安全 Java
如何理解Java中的并发?
Java并发指多任务交替执行,提升资源利用率与响应速度。通过线程实现,涉及线程安全、可见性、原子性等问题,需用synchronized、volatile、线程池及并发工具类解决,是高并发系统开发的关键基础。(238字)
319 5
|
8月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
418 83
|
11月前
|
消息中间件 算法 安全
JUC并发—1.Java集合包底层源码剖析
本文主要对JDK中的集合包源码进行了剖析。
|
8月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
455 83
|
10月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
367 0
|
8月前
|
SQL 缓存 安全
深度理解 Java 内存模型:从并发基石到实践应用
本文深入解析 Java 内存模型(JMM),涵盖其在并发编程中的核心作用与实践应用。内容包括 JMM 解决的可见性、原子性和有序性问题,线程与内存的交互机制,volatile、synchronized 和 happens-before 等关键机制的使用,以及在单例模式、线程通信等场景中的实战案例。同时,还介绍了常见并发 Bug 的排查与解决方案,帮助开发者写出高效、线程安全的 Java 程序。
434 0
|
9月前
|
Java 物联网 数据处理
Java Solon v3.2.0 史上最强性能优化版本发布 并发能力提升 700% 内存占用节省 50%
Java Solon v3.2.0 是一款性能卓越的后端开发框架,新版本并发性能提升700%,内存占用节省50%。本文将从核心特性(如事件驱动模型与内存优化)、技术方案示例(Web应用搭建与数据库集成)到实际应用案例(电商平台与物联网平台)全面解析其优势与使用方法。通过简单代码示例和真实场景展示,帮助开发者快速掌握并应用于项目中,大幅提升系统性能与资源利用率。
253 6
Java Solon v3.2.0 史上最强性能优化版本发布 并发能力提升 700% 内存占用节省 50%
|
10月前
|
缓存 安全 Java
【高薪程序员必看】万字长文拆解Java并发编程!(3-1):并发共享问题的解决与分析
活锁:多个线程相互影响对方退出同步代码块的条件而导致线程一直运行的情况。例如,线程1的退出条件是count=5,而线程2和线程3在其代码块中不断地是count进行自增自减的操作,导致线程1永远运行。内存一致性问题:由于JIT即时编译器对缓存的优化和指令重排等造成的内存可见性和有序性问题,可以通过synchronized,volatile,并发集合类等机制来解决。这里的线程安全是指,多个线程调用它们同一个实例的方法时,是线程安全的,但仅仅能保证当前调用的方法是线程安全的,不同方法之间是线程不安全的。
174 0