Java 线程面试题总结2022

作者:半身风雪

简介:连续写了七篇【Java多线程系列】的知识点,如果没有写完全,请留言指点,我们一起共同学习探讨。

第一篇:Java线程之间的共享和协作
第二篇:Java 天生就是多线程
第三篇:ThreadLocal进阶解析
第四篇:CAS 原理解析
第五篇:阻塞队列原理解析
第六篇:线程池原理解析
第七篇:并发编程归纳总结


一、sychronied 修饰普通方法和静态方法的区别?什么是可见性?

       对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的 class 对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个 class 对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。
       有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存 在的,类锁其实锁的是每个类的对应的 class 对象。类锁和对象锁之间也是互不干扰的。
       可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
       由于线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量,那么对于共享变量 V,它们首先是在自己的工作内存,之后再同步到主内存。可是并不会及时的刷到主存中,而是会有一定时间差。很明显,这个时候线程 A 对变量 V 的操作对于线程 B 而言就不具备可见性了 。
       要解决共享对象可见性这个问题,我们可以使用 volatile 关键字或者是加锁。

二、锁分为哪几类。

在这里插入图片描述

三、CAS 无锁编程的原理。

       使用当前的处理器基本都支持 CAS()的指令,只不过每个厂家所实现的算法并不一样,每一个 CAS 操作过程都包含三个运算符:一个内存地址 V,一个期望的值 A 和一个新值 B,操作的时候如果这个地址上存放的值等于这个期望的值 A,则将地址上的值赋为新值 B,否则不做任何操作。
       CAS 的基本思路就是,如果这个地址上的值和期望的值相等,则给其赋予新值, 否则不做任何事儿,但是要返回原值是多少。循环 CAS 就是在一个循环里不断的 做 cas 操作,直到成功为止。 还可以说说 CAS 的三大问题。

四、ReentrantLock 的实现原理。

       线程可以重复进入任何一个它已经拥有的锁所同步着的代码块, synchronized、ReentrantLock 都是可重入的锁。在实现上,就是线程每次获取锁时判定如果获得锁的线程是它自己时,简单将计数器累积即可,每释放一次锁, 进行计数器累减,直到计算器归零,表示线程已经彻底释放锁。底层则是利用了 JUC 中的 AQS 来实现的。

五、AQS 原理。

       是用来构建锁或者其他同步组件的基础框架,比如 ReentrantLock、 ReentrantReadWriteLock 和 CountDownLatch 就是基于 AQS 实现的。它使用了一 个 int 成员变量表示同步状态,通过内置的 FIFO 队列来完成资源获取线程的排队工作。它是 CLH 队列锁的一种变体实现。它可以实现 2 种同步方式:独占式,共 享式。
       AQS 的主要使用方式是继承,子类通过继承 AQS 并实现它的抽象方法来管理同步状态,同步器的设计基于模板方法模式,所以如果要实现我们自己的同步工具类就需要覆盖其中几个可重写的方法,如 tryAcquire、tryReleaseShared 等等。
       这样设计的目的是同步组件(比如锁)是面向使用者的,它定义了使用者与同步组件交互的接口(比如可以允许两个线程并行访问),隐藏了实现细节;同步器面向的是锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。这样就很好地隔离了使用者和实现者所需关注的领域。
       在内部,AQS 维护一个共享资源 state,通过内置的 FIFO 来完成获取资源线程的排队工作。该队列由一个一个的 Node 结点组成,每个 Node 结点维护一个 prev 引用和 next 引用,分别指向自己的前驱和后继结点,构成一个双端双向链 表。

六、Synchronized 的原理以及与ReentrantLock 的区别。

       synchronized (this)原理:涉及两条指令:monitorenter,monitorexit;再说同步方法,从同步方法反编译的结果来看,方法的同步并没有通过指令 monitorenter 和 monitorexit 来实现,相对于普通方法,其常量池中多了 ACC_SYNCHRONIZED 标示符。
       JVM 就是根据该标示符来实现方法的同步的:当方法被调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取 monitor,获取成功之后才能执行方法体,方法执行完后再释放 monitor。在方法执行期间,其他任何线程都无法再获得同一个 monitor 对象。

七、Synchronized 做了哪些优化

       引入如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁、逃逸分析等技术来减少锁操作的开销。

  • 逃逸分析:如果证明一个对象不会逃逸方法外或者线程外,则可针对此变量进行优化:
  • 同步消除: synchronization Elimination,如果一个对象不会逃逸出线程,则对此变量的同步措施可消除。
  • 锁消除和粗化锁消除:虚拟机的运行时编译器在运行时如果检测到一些要求同步的代码上不可能发生共享数据竞争,则会去掉这些锁。
  • 锁粗化:将临近的代码块用同一个锁合并起来。 消除无意义的锁获取和释放,可以提高程序运行性能。

八、Synchronized static 与非 static 锁的区别和范围。

       对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的 class 对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个 class 对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。
       有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存 在的,类锁其实锁的是每个类的对应的 class 对象。类锁和对象锁之间也是互不干扰的。

九、volatile 能否保证线程安全?在 DCL 上的作用是什么?

       不能保证,在 DCL 的作用是:volatile 是会保证被修饰的变量的可见性和有序性, 保证了单例模式下,保证在创建对象的时候的执行顺序一定是

  1. 分配内存空间
  2. 实例化对象 instance
  3. 把 instance 引用指向已分配的内存空间,此时 instance 有了内存地址,不再为 null 了的步骤,从而保证了 instance 要么为 null 要么是已经完全初始化好的对象。

十、volatile 和 synchronize 有什么区别?

       volatile 是最轻量的同步机制。 volatile 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。但是 volatile 不能保证操作的原子性,因此多线程下的写复合操作会导致线程安全问题。
       关键字 synchronized 可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性,又称为内置锁机制。

十一、什么是守护线程?你是如何退出一个线程的?

       Daemon(守护)线程是一种支持型线程,因为它主要被用作程序中后台调 度以及支持性工作。这意味着,当一个 Java 虚拟机中不存在非 Daemon 线程的时候,Java 虚拟机将会退出。可以通过调用 Thread.setDaemon(true)将线程设置为 Daemon 线程。我们一般用不上,比如垃圾回收线程就是 Daemon 线程。
       线程的中止:要么是 run 执行完成了,要么是抛出了一个未处理的异常导致线程提前结束。 暂停、恢复和停止操作对应在线程 Thread 的 API 就是 suspend()、resume()和 stop()。 但是这些 API 是过期的,也就是不建议使用的。因为会导致程序可能工作在不确定状态下。
       安全的中止则是其他线程通过调用某个线程 A 的 interrupt()方法对其进行中断操作,被中断的线程则是通过线程通过方法 isInterrupted()来进行判断是否被中断, 也可以调用静态方法 Thread.interrupted()来进行判断当前线程是否被中断,不过 Thread.interrupted()会同时将中断标识位改写为 false。

十二、sleep 、wait、yield 的区别,wait 的线程如何唤醒它?

       yield()方法:使当前线程让出 CPU 占有权,但让出的时间是不可设定的。也不会释放锁资源。所有执行 yield()的线程有可能在进入到就绪状态后会被操作系统再次选中马上又被执行。
       yield() 、sleep()被调用后,都不会释放当前线程所持有的锁。
       调用 wait()方法后,会释放当前线程持有的锁,而且当前被唤醒后,会重新去竞争锁,锁竞争到后才会执行 wait 方法后面的代码。
       Wait 通常被用于线程间交互,sleep 通常被用于暂停执行,yield()方法使当前线程让出 CPU 占有权。
       wait 的线程使用 notify/notifyAll()进行唤醒。

十三、sleep 是可中断的么?

       sleep 本身就支持中断,如果线程在 sleep 期间被中断,则会抛出一个中断异常。

十四、线程生命周期。

Java 中线程的状态分为 6 种:

  1. 初始(NEW):新创建了一个线程对象,但还没有调用 start()方法。
  2. 运行(RUNNABLE):Java 线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。 线程对象创建后,其他线程(比如 main 线程)调用了该对象的 start()方法。 该状态的线程位于可运行线程池中,等待被线程调度选中,获取 CPU 的使用权, 此时处于就绪状态(ready)。就绪状态的线程在获得 CPU 时间片后变为运行中 状态(running)。
  3. 阻塞(BLOCKED):表示线程阻塞于锁。
  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作 (通知或中断)。
  5. 超时等待(TIMED_WAITING):该状态不同于 WAITING,它可以在指定的时 间后自行返回。
  6. 终止(TERMINATED):表示该线程已经执行完毕。

在这里插入图片描述

十五、ThreadLocal 是什么?

       ThreadLocal 是 Java 里一种特殊的变量。ThreadLocal 为每个线程都提供了变量的副本,使得每个线程在某一时间访问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享。
       在内部实现上,每个线程内部都有一个 ThreadLocalMap,用来保存每个线程所拥有的变量副本。

十六、线程池基本原理。

       在开发过程中,合理地使用线程池能够带来 3 个好处。
第一:降低资源消耗。
第二:提高响应速度。
第三:提高线程的可管理性。

  1. 如果当前运行的线程少于 corePoolSize,则创建新线程来执行任务(注意, 执行这一步骤需要获取全局锁)。
  2. 如果运行的线程等于或多于 corePoolSize,则将任务加入 BlockingQueue。
  3. 如果无法将任务加入 BlockingQueue(队列已满),则创建新的线程来处理任务。
  4. 如果创建新线程将使当前运行的线程超出 maximumPoolSize,任务将被拒绝,并调用 RejectedExecutionHandler.rejectedExecution()方法。

十七、有三个线程 T1,T2,T3,怎么确保它们按顺序执行?

       可以用 join 方法实现。

总结

这些面试题,应该就是现在经常被问到的一些问题了吧,整体这些东西,真的太麻烦了。

原文地址:https://blog.csdn.net/u010755471

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


AdvserView.java package com.earen.viewflipper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory;
ImageView的scaleType的属性有好几种,分别是matrix(默认)、center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY。 |值|说明| |:--:|:--| |center|保持原图的大小,显示在ImageVie
文章浏览阅读8.8k次,点赞9次,收藏20次。本文操作环境:win10/Android studio 3.21.环境配置 在SDK Tools里选择 CMAKE/LLDB/NDK点击OK 安装这些插件. 2.创建CMakeLists.txt文件 在Project 目录下,右键app,点击新建File文件,命名为CMakeLists.txt点击OK,创建完毕! 3.配置文件 在CMa..._link c++ project with gradle
文章浏览阅读1.2w次,点赞15次,收藏69次。实现目的:由mainActivity界面跳转到otherActivity界面1.写好两个layout文件,activity_main.xml和otherxml.xmlactivity_main.xml<?xml version="1.0" encoding="utf-8"?><RelativeLayout ="http://schemas..._android studio 界面跳转
文章浏览阅读3.8w次。前言:最近在找Android上的全局代理软件来用,然后发现了这两款神作,都是外国的软件,而且都是开源的软件,因此把源码下载了下来,给有需要研究代理这方面的童鞋看看。不得不说,国外的开源精神十分浓,大家相互使用当前基础的开源软件,然后组合成一个更大更强的大开源软件。好吧,废话不多说,下面简单介绍一下这两款开源项目。一、ProxyDroid:ProxyDroid功能比较强大,用到的技术也比较多,源码也_proxydroid
文章浏览阅读2.5w次,点赞17次,收藏6次。创建项目后,运行项目时Gradle Build 窗口却显示错误:程序包R不存在通常情况下是不会出现这个错误的。我是怎么遇到这个错误的呢?第一次创建项目,company Domain我使用的是:aven.com,但是创建过程在卡在了Building 'Calculator' Gradle Project info这个过程中,于是我选择了“Cancel”第二次创建项目,我还是使用相同的项目名称和项目路_r不存在
文章浏览阅读8.9w次,点赞4次,收藏43次。前言:在Android上使用系统自带的代理,限制灰常大,仅支持系统自带的浏览器。这样像QQ、飞信、微博等这些单独的App都不能使用系统的代理。如何让所有软件都能正常代理呢?ProxyDroid这个软件能帮你解决!使用方法及步骤如下:一、推荐从Google Play下载ProxyDroid,目前最新版本是v2.6.6。二、对ProxyDroid进行配置(基本配置:) (1) Auto S_proxydroid使用教程
文章浏览阅读1.1w次,点赞4次,收藏17次。Android Studio提供了一个很实用的工具Android设备监视器(Android device monitor),该监视器中最常用的一个工具就是DDMS(Dalvik Debug Monitor Service),是 Android 开发环境中的Dalvik虚拟机调试监控服务。可以进行的操作有:为测试设备截屏,查看特定进程中正在运行的线程以及堆栈信息、Logcat、广播状态信息、模拟电话_安卓摄像头调试工具
文章浏览阅读2.1k次。初学Android游戏开发的朋友,往往会显得有些无所适从,他们常常不知道该从何处入手,每当遇到自己无法解决的难题时,又往往会一边羡慕于 iPhone下有诸如Cocos2d-iphone之类的免费游戏引擎可供使用,一边自暴自弃的抱怨Android平台游戏开发难度太高,又连个像样的游 戏引擎也没有,甚至误以为使用Java语言开发游戏是一件费力不讨好且没有出路的事情。事实上,这种想法完全是没有必_有素材的游戏引擎
文章浏览阅读3.2k次,点赞2次,收藏2次。2014年12月从csdn专家福利获得的一本书《Android游戏开发技术实战详解》,尘封了一年多的时间,今天才翻开来看。我认识中的Android,提到Android最先浮现在我脑海中的是那可爱的机器人图标:这个Logo是由Ascender公司设计的,诞生于2010年,其设计灵感源于男女厕所门上的图形符号(真的是灵感无处不在),于是布洛克绘制了一个简单的机器人,它的躯干就像锡罐的形状,头上还有两根_智能手机的特点有哪些?
文章浏览阅读8.1k次,点赞9次,收藏11次。首先,Android是不是真的找工作越来越难呢?这个可能是大家最关心的。这个受大的经济环境以及行业发展前景的影响,同时也和个人因素有关。2016-08-26近期一方面是所在的公司招聘Java开发人员很难招到合适的,投简历的人很少;而另一方面,经常听身边的人说Android、iOS方面找工作不好找,特别是没什么经验的,经验比较少的!说是不好找,但在我家所在的吉林省省会长春,会Unity3D+Maya_android 开发和asp.net哪个好 site:blog.csdn.net
文章浏览阅读6.1k次。在上篇“走进Android开发的世界,HelloWorld”,我们创建了一个Android 项目 HelloWorld,并演示了如何通过USB连接手机查看运行效果;而如果没有手机或没有对应型号的手机,又想做对应型号(屏幕尺寸、Android系统版本)的适配,应该怎么办呢?这时Android模拟器就派上用场了。Android模拟器Android SDK自带一个移动模拟器。它是一个可以运行在你电脑上的_安卓移动开发软件怎样预览
文章浏览阅读8.9k次。Google IO 2017 上宣布,将Kotlin语言作为安卓开发的官方语言。Kotlin由JetBrains公司开发,与Java 100%互通,并具备诸多Java尚不支持的新特性。谷歌称还将与JetBrains公司合作,为Kotlin设立一个非盈利基金会。Kotlin 是一个基于 JVM 的静态类型编程语言,Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JV_kotlin为什么被嫌弃
文章浏览阅读9.6w次,点赞17次,收藏35次。有些情况下,不方便使用断点的方式来调试,而是希望在控制台打印输出日志,使用过Eclipse的同学都知道Java可以使用 System.out.println(""); 来在控制台打印输出日志,但是在android studio中却是不行的,还是有差别的,那应该用什么呢?android.util.Log在调试代码的时候我们需要查看调试信息,那我们就需要用Android Log类。android.ut_andirod.studio 为什么不在控制台打印输出
文章浏览阅读8.2k次,点赞2次,收藏8次。在上篇“走进Android开发的世界,HelloWorld”,我们创建了一个Android 项目 HelloWorld,并演示了如何通过USB连接手机查看运行效果;这里讲一下如何为应用添加一个按钮,并为按钮添加Click单击事件处理程序,显示/隐藏另一个按钮。添加按钮在HelloWorld项目的基础上,打开界面布局文件:activity_main.xml切换到Design(设计)模式;在组件But_activity_main.xml按钮隐藏
文章浏览阅读2.9k次,点赞3次,收藏9次。android 开发工具主流的还是Android Studio,当然也有很多人喜欢用Eclipse,也有人喜欢用IntelliJ IDEA ;还有Xamarin这种只需要编写一次代码,可以编译多种平台可运行的强大工具。但是它又真的强大吗?就我看来没有,身边很多人还是在用Android Studio、XCode开发应用,没见谁在用Xamarin之类的工具。系统要求WindowsMicrosoft®_android开发下载安装
文章浏览阅读4.2k次,点赞7次,收藏26次。你知道Hello World程序的由来吗?对于大多数编程语言的学习来说,真正入门的一课就是 Hello World!会而不难,难而不会。虽然很多人写过关于Android开发Hello World的文章,但随着时间的推移,开发工具、技术的进步,可能有些已经过时了。我就记录一下当下我所经历的第一个Android APP HelloWorld。一、准备1、开发环境参考:Android Studio 下载_android helloworld textview 句柄获取
这篇“android轻量级无侵入式管理数据库自动升级组件怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定...
今天小编给大家分享一下Android实现自定义圆形进度条的常用方法有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文...
这篇文章主要讲解了“Android如何解决字符对齐问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Android...