java面试题-多线程-1

1、并发编程三要素?

并发编程的三要素包括共享数据、互斥访问和同步机制。

1.1 共享数据

共享数据是指在多个线程之间共享的数据资源。多个线程可以同时读取和修改共享数据。共享数据在并发编程中可能引发以下问题:

数据竞争:当多个线程同时修改共享数据时,可能导致不可预期的结果,程序的行为可能不再是确定性的,依赖于线程的执行顺序和时间。

数据一致性:当多个线程同时读取和修改共享数据时,可能导致数据不一致的问题,例如一个线程读取了一个尚未被另一个线程修改的共享数据的旧值。

1.2 互斥访问

互斥访问是指在多个线程之间对共享数据的访问进行互斥控制,保证同一时间只有一个线程可以对共享数据进行读取或修改。通过互斥访问,可以避免数据竞争和数据一致性的问题。常用的互斥控制机制包括:

互斥锁:通过使用互斥锁,保证同一时间只有一个线程可以获得锁并访问共享数据,其他线程需要等待锁的释放才能访问共享数据。

读写锁:允许多个线程同时读取共享数据,但只允许一个线程进行写操作,这样可以提高读取操作的并发性能,同时保证写操作的原子性和线程安全性。

1.3 同步机制

同步机制是指在多个线程之间进行协调和同步操作,以保证共享数据的一致性和正确性。同步机制可以通过控制线程的执行顺序和时机来确保共享数据的正确访问。常用的同步机制包括:

条件变量:用于线程之间的通信和等待,一个线程可以等待某个条件变量的触发,而另一个线程可以在满足特定条件时触发条件变量,从而唤醒等待的线程继续执行。

信号量:用来实现线程的互斥和同步操作,通过控制信号量的值和使用wait和signal操作,实现线程的阻塞和唤醒。

来源链接 : 并发编程三要素:共享数据、互斥访问和同步机制

2、同步方法和同步块哪个是更好的选择?

同步块通常是更好的选择,因为它不会锁住整个对象,而是可以灵活控制锁的范围,从而提高并发性能并减少死锁的风险。具体来说:

锁的范围

同步方法:会锁住整个对象,即使类中只有部分方法需要同步,也会导致整个对象被锁定,这可能导致性能下降,尤其是在多线程环境下12。

同步块:可以锁定代码块,而不是整个对象。这意味着只有需要同步的代码部分会被锁定,减少了锁的范围,从而提高了并发性能12。

性能和并发性能

同步方法:由于锁的范围较大,可能会导致线程频繁等待锁的释放,影响并发性能12。

同步块:通过精确控制锁的范围,减少了线程等待的时间,提高了并发性能12。

使用场景

同步方法:适合于整个方法都需要同步的情况,代码简洁明了3。

同步块:适合于只需要对部分代码进行同步的情况,或者需要对多个对象进行同步时更为灵活和高效3。

3、谈谈原子性?哪些使用到了?

原子性(Atomicity)是指一个操作在执行过程中不可分割,要么完全执行,要么完全不执行。在并发编程和多线程环境中,原子性操作不会被其他线程的操作打断,从而确保操作的完整性和一致性。

原子性的定义和重要性

在并发编程中,原子性确保了临界区域的代码在执行过程中不会被其他线程干扰,从而避免了数据竞争和不一致的问题。例如,在银行转账操作中,从账户A扣除金额和向账户B增加金额这两个操作必须作为一个整体执行,否则可能会导致资金错误12。

原子性的实现方式

synchronized关键字:在Java中,可以使用synchronized关键字来确保一段代码在同一时刻只能被一个线程执行,从而保证操作的原子性。例如:

javaCopy Code

synchronizedvoidincrease(){    i++;}

Lock接口:Java的java.util.concurrent包提供了Lock接口,通过显式锁定和解锁机制来实现同步,例如使用ReentrantLock:

javaCopy Code

Locklock=newReentrantLock();lock.lock();try{    i++;}finally{    lock.unlock();}

Atomic类:Java的java.util.concurrent.atomic包提供了一系列原子类,如AtomicInteger,可以确保对整数的操作是原子的:

javaCopy Code

AtomicIntegeratomicInteger=newAtomicInteger(0);atomicInteger.incrementAndGet();

内存屏障:在某些情况下,可以使用内存屏障(Memory Barrier)来确保指令的执行顺序,从而保证操作的原子性。

原子性在其他领域的应用

数据库事务:在数据库管理中,原子性是ACID属性之一,确保事务中的所有操作要么全部提交,要么全部回滚。数据库通过日志记录和恢复技术来实现原子性3。

多核处理器指令集:在一些处理器架构中,对某些类型的操作(如读取或写入特定大小的内存块)提供了原子性支持。例如,在32位系统上,对32位整数的读写通常是原子的4。

参考:内存屏障      并发编程三要素:原子性,有序性,可见性

4、谈谈可见性?哪些使用到了?

在并发编程中,可见性是指多个线程访问共享变量时,一个线程修改的状态能够立即对其他线程可见。由于多核CPU的缓存机制,可能会导致数据不一致的问题。

解决可见性的方式包括使用volatile关键字、synchronized锁和Lock机制。volatile关键字确保变量修改后立即同步到主内存,禁止指令重排序;synchronized在锁定和解锁时同步缓存;Lock则是基于volatile并手动控制锁状态实现同步2。

参考:并发编程三大特性:2、可见性(什么是可见性、解决可见性的方式)

           Volatile、Synchronized、ReentrantLock锁机制使用说明

           volatile的底层是lock#指令还是内存屏障?

           多线程基础概念

5、强引用、弱引用、软引用、虚引用

参考:强引用、弱引用、软引用、虚引用

6、ThreadLocal 实现原理 与 如何使用弱引用解决内存泄漏问题

参考:Java ThreadLocal 实现原理 与 如何使用弱引用解决内存泄漏问题

           ThreadLocal 源码解析:巧用弱引用解决内存泄漏问题

7、谈谈有序性?举一个例子?

参考:详解线程的三大特性:原子性、可见性和有序性

           多线程篇(可见性 & 原子性 & 有序性(可见性))

8、什么是线程池

参考:线程池详解(建议收藏)

           线程池入门知识

9、谈谈四种线程池的创建?

参考:java四种线程池创建

10、多线程的优缺点?

提高程序性能:多线程允许程序在多核处理器上并行执行多个任务,充分利用现代多核CPU的计算能力。通过将任务分割成可以并行执行的子任务,并将这些子任务分配给多个线程,程序可以显著缩短任务的执行时间12。

提高响应速度:在图形用户界面(GUI)应用中,多线程可以使主线程处理用户输入,而其他线程执行耗时的后台任务,从而提高程序的响应速度和用户体验23。

改善程序设计:多线程可以使程序更加灵活和可靠。例如,在服务器程序中,多线程可以处理多个客户端请求,保证程序的稳定性和可靠性4。

多线程的缺点包括

内存占用增加:线程越多,占用的内存也越多,这可能会导致系统资源紧张5。

管理开销增加:线程的协调和管理需要额外的CPU开销,增加了程序的复杂性和潜在的错误率5。

资源共享问题:多线程环境中需要解决资源共享的问题,不当的资源共享可能导致数据不一致或其他异常情况2。

参考:Java干货 | 多线程的优势与劣势问题

11、创建线程的有哪些方式?

继承Thread类:创建一个类继承Thread类,并重写run()方法来实现线程的执行逻辑。然后创建该类的实例并调用start()方法启动线程。这种方式简单直观,适合初学者学习线程的基本原理,但扩展性较差,因为Java不支持多继承12。

实现Runnable接口:创建一个类实现Runnable接口,实现run()方法来实现线程的执行逻辑。然后创建Thread类的实例,将实现了Runnable接口的类实例作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。这种方式任务与代码分离,适合多线程共享执行相同任务的情况12。

使用匿名内部类:可以在创建线程的同时定义并实现线程的执行逻辑,使用匿名内部类的方式。这种方式代码简洁,适用于简单的线程创建1。

使用Lambda表达式:在Java 8及以上版本中,可以使用Lambda表达式来简化线程的创建。这种方式代码更加简洁,适用于简单的线程任务1。

实现Callable接口:通过实现Callable接口,实现call()方法来实现线程的执行逻辑。然后创建FutureTask对象,将Callable对象传递给FutureTask的构造函数,最后通过FutureTask对象的get()方法获取执行结果。这种方式可以获取线程的执行结果,适用于需要返回执行结果的场景34。

使用线程池:通过线程池来创建和管理线程。线程池提供了多种创建和管理线程的方式,可以提高资源利用率和减少线程创建的开销。适用于需要大量并发处理的任务4。

使用定时器工具类:通过定时器工具类(如TimerScheduledExecutorService)来创建定时执行的线程任务。这种方式适用于需要定时执行的任务4。

参考:线程创建方式

12、谈谈各种创建线程的优缺点?

参考:多种方式创建线程,优缺点全解析!

13、Runnable和Callable的区别?

返回值

Runnable:其run()方法没有返回值。适用于不需要返回结果的任务12。

Callable:其call()方法可以返回一个泛型指定的值,并且可以抛出受检异常(checked exception)。适用于需要返回结果或处理异常的复杂任务12。

异常处理

Runnable:run()方法不能抛出受检异常,所有的异常需要在方法内部处理或通过其他机制传递12。

Callable:call()方法可以抛出受检异常,异常可以在外部被捕获和处理,或者通过Future对象的get()方法捕获12。

使用方式

Runnable:通常与Thread类结合使用,通过Thread的构造函数传递Runnable实例来创建线程24。

Callable:通常与线程池(如ExecutorService)结合使用,线程池会返回一个Future对象,用于表示异步计算的结果,通过Future对象可以获取计算结果或捕获异常24。

适用场景

Runnable:适用于不需要返回值且不会抛出受检异常的简单任务,如简单的打印输出、修改共享变量等24。

Callable:适用于需要返回值或需要处理异常的复杂任务,如计算结果处理、网络或IO操作等,常用于实现异步任务处理以提高系统吞吐量和响应速度24。

14、线程在执行过程中的状态是如何流转的?

参考:线程在执行过程中的状态是如何流转的?

           Java线程的6种状态及转化方式

           图解Java线程状态

15、LockSupport.park与LockSupport.unpark的底层原理

参考:Java并发基础:LockSupport.park与LockSupport.unpark的底层原理

16、互斥锁(mutex)的底层原理是什么? 操作系统具体是怎么实现的?

参考:互斥锁(mutex)的底层原理是什么? 操作系统具体是怎么实现的?

17、常用的并发集合类有哪些?

参考:Java 中 一些常见的并发集合类

18、ConcurrentHashMap实现

参考:最爱问的高频ConcurrentHashMap原理,你会了吗?

           面试系列之-ConcurrentHashMap实现原理

19、ConcurrentHashMap 为何不能插入 null?HashMap 为何可以?

参考:为什么HashMap的键值可以为null,而ConcurrentHashMap不行?

           ConcurrentHashMap 为何不能插入 null?HashMap 为何可以?

           HashMap很美好,但线程不安全怎么办?ConcurrentHashMap告诉你答案

20、HashMap万字解析

参考:耗时3天写完的HashMap万字解析,争取一篇文章讲透它,面试官看了都直点头

           面试官:来,问你几个关于HashMap的问题

           面试官上来就让手撕HashMap的7种遍历方式,当场愣住,最后只写出了3种

21、CopyOnWriteArrayList实现

参考:深入解析 `CopyOnWriteArrayList` 的实现原理与应用

22、CopyOnWriteArraySet实现

参考:CopyOnWriteArrayList和 CopyOnWriteArraySet 怎么实现线程安全

23、常用的并发工具类有哪些

参考:CountDownLatch、Semaphore等4大并发工具类详解 

         【死磕Java并发】常用并发工具类详解

24、CyclicBarrier和CountDownLatch的应用场景

CyclicBarrier的应用场景

并行任务同步:CyclicBarrier常用于需要多个线程或任务并行执行到某个点后同步进行的场景。例如,在跑步比赛中,所有参赛者需要热身完毕才能同时起跑1。

数据汇总和处理:在多线程计算数据后,CyclicBarrier可以用于合并计算结果。例如,计算5个人的月平均工资,每个线程计算一个人的工资,所有线程计算完毕后合并结果2。

重复执行的循环任务:CyclicBarrier的可重复使用特性使其适用于需要多次重复执行的循环任务。每次任务完成后,所有线程再次等待并重新执行,直到满足某些条件3。

CountDownLatch的应用场景

串行任务等待:CountDownLatch适用于需要等待一组操作按顺序完成的场景。例如,电商平台的订单处理系统,需要完成库存检查、支付验证、优惠券验证等多个独立任务后才能确认订单4。

初始化操作:在服务器或应用程序启动时,CountDownLatch可以用于确保某些初始化操作完成后再进行下一步。例如,服务器在接受请求前必须完成依赖服务的初始化5。

区别

可重复使用性:CyclicBarrier是可重复使用的,当所有线程到达屏障后,它会重置并允许再次使用;而CountDownLatch是一次性的,一旦计数达到零,就不能再次使用3。

参考:CountDownLatch 和 CyclicBarrier 使用场景详解

25、CyclicBarrier和CountDownLatch的区别

参考:CountDownLatch 和 CyclicBarrier的区别与详解

26、Semaphore的应用场景

参考:Semaphore实现原理全面解析

27、synchronized的作用?底层如何实现?

参考:深入理解Synchronized的使用和实现原理

           synchronized底层原理

          52.说一下 synchronized 底层实现原理?_synchronized底层实现

          Synchronized实现原理,你知道多少?

          深入理解synchronized工作原理与锁的优化

          synchronized锁的升级流程

28、synchronized和ReentrantLock的区别

synchronized和ReentrantLock的主要区别在于获取和释放锁的方式、锁的公平性、响应中断的能力以及底层实现机制等方面。

获取和释放锁的方式

synchronized:synchronized是隐式锁,当进入synchronized修饰的代码块或方法时自动加锁,离开时自动释放锁。这种方式不需要手动管理锁的获取和释放12。

ReentrantLock:ReentrantLock是显式锁,需要手动调用lock()方法获取锁,并在使用完毕后调用unlock()方法释放锁。这种方式需要程序员显式管理锁的生命周期12。

锁的公平性

synchronized:synchronized是非公平锁,不能保证等待时间最长的线程最先获取锁12。

ReentrantLock:ReentrantLock默认是非公平锁,但可以通过构造函数设置为公平锁。公平锁会按照线程请求锁的顺序来分配锁,保证等待时间最长的线程最先获取锁12。

响应中断的能力

synchronized:不能响应中断,如果发生死锁,线程会一直等待下去23。

ReentrantLock:可以通过lockInterruptibly()方法获取锁并响应中断,如果在等待过程中线程被中断,则会抛出InterruptedException,从而允许线程提前退出等待状态23。

底层实现机制

synchronized:是Java内置的关键字,通过JVM层面的监视器(Monitor)实现,采用悲观并发策略4。

ReentrantLock:基于AbstractQueuedSynchronizer(AQS)框架实现,是一个高级的同步工具类,采用乐观并发策略4。

其他特性

ReentrantLock提供了更多的功能,如尝试非阻塞获取锁(tryLock())、设置超时时间、判断锁是否被其他线程持有等12。

参考:synchronized和ReentrantLock有什么区别

           死磕 java同步系列之ReentrantLock VS synchronized——结果有点出乎意外

          高并发时为什么推荐ReentrantLock而不是synchronized

29、jvm 虚拟线程

参考:[Java基础]虚拟线程

30、什么是CAS?底层如何实现

参考:CAS底层原理示例+详解,终于弄清楚了!

31、什么是Future?底层如何实现

参考:JUC基础(二)—— Future接口 及其实现

            并发-第23课:Future

           Java并发编程:Future 的底层实现原理

32、什么是AQS?底层如何实现?

  参考: AQS实现原理

              并发编程系列-从底层源码剖析AQS的来龙去脉!(通俗易懂)

             一篇文章彻底搞懂AQS底层原理

             万长文字 | 16张图解开AbstractQueuedSynchronizer

33、ReadWriteLock底层实现

参考:面试官:说说读写锁的底层实现

           ReentrantReadWriteLock读写锁

34、死锁的常见原因有哪些

参考:死锁产生的原因和处理死锁的方法(全网最详细!!!!)

          死锁的处理策略—预防死锁、避免死锁、检测和解除死锁

35、怎么唤醒一个阻塞的线程

参考:问:JAVA中唤醒阻塞的线程有哪些?

36、什么是多线程的上下文切换

参考:什么是多线程中的上下文切换

          面试官:什么是多线程中的上下文切换?

37、线程调度算法是什么?

参考:程序员必读!深入解析Java线程调度算法神秘面纱!

           计算机系统:线程调度

38、什么是线程调度器和时间分片

参考:什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing)?

39、单例模式线程安全性?

参考:线程安全的单例模式

40、谈谈ExecutorService,ScheduledExecutorService

参考:Java线程池深度剖析:从Executor到ScheduledExecutorService

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容