当前位置:   article > 正文

Java并发高频面试题(2024最新版)_java 并发面试题

java 并发面试题

目录

1.线程的生命周期?线程有几种状态?

2.sleep()、wait()、join()、yield()之间的的区别?

3.Thread和Runable的区?

4.并发、并行、串行之间的区别?

5.并发的三大特性?

6.Java死锁如何避免?

7.为什么用线程池?解释下线程池参数?

8.线程池的底层⼯作原理

9.ReentrantLock中tryLock()和lock()⽅法的区别

10.Sychronized和ReentrantLock的区别

11.谈谈你对AQS的理解,AQS如何实现可重入锁?

12.ThreadLocal的底层原理


1.线程的生命周期?线程有几种状态?
线程通常有五种状态,创建,就绪,运⾏、阻塞和死亡状态:
        1. 新建状态(New) :新创建了一个线程对象。
        2. 就绪状态(Runnable) :线程对象创建后,其他线程调⽤了该对象的start⽅法。该状态的线程位于可运⾏线程池中,变得可运行,等待获取CPU的使⽤权。
        3. 运⾏状态(Running) :就绪状态的线程获取了CPU,执⾏程序代码。
        4. 阻塞状态(Blocked) :阻塞状态是线程因为某种原因放弃CPU使⽤权,暂时停止运行。直到线程进⼊就绪状态,才有机会转到运行状态。
        5. 死亡状态(Dead) :线程执行完了或者因异常退出了run⽅法,该线程结束⽣命周期。
阻塞的情况⼜分为三种:
        1. 等待阻塞 :运⾏的线程执行wait方法,该线程会释放占⽤的所有资源,JVM会把该线程放⼊“等待池”中。进⼊这个状态后,是不能自动唤醒的,必须依靠其他线程调⽤notify或notifyAll方法才能被唤醒,wait是object类的方法
        2. 同步阻塞 :运行的线程在获取对象的同步锁时,若该同步锁被别的线程占⽤,则JVM会把该线程放⼊“锁池”中。
        3. 其他阻塞 :运行的线程执行sleep或join方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep状态超时、join等待线程终⽌或者超时、或者I/O处理完毕时,线程重新转⼊就绪状态。sleep是Thread类的方法。
2.sleep()、wait()、join()、yield()之间的的区别?

锁池:所有需要竞争同步锁的线程都会放在锁池当中,比如当前对象的锁已经被其中一个线程得到,则其他线程需要在这个锁池进⾏等待,当前⾯的线程释放同步锁后锁池中的线程去竞争同步锁,当某个线 程得到后会进⼊就绪队列进行等待cpu资源分配。
等待池:当我们调⽤wait()方法后,线程会放到等待池当中,等待池的线程是不会去竞争同步锁。只有调用了notify()或notifyAll()后等待池的线程才会开始去竞争锁,notify()是随机从等待池选出一个线程放到锁池,⽽notifyAll()是将等待池的所有线程放到锁池当中。
        1. sleep 是 Thread 类的静态本地⽅法,wait 则是 Object 类的本地⽅法。
        2. sleep⽅法不会释放lock,但是wait会释放,而且会加⼊到等待队列中。
  1. sleep就是把cpu的执⾏资格和执⾏权释放出去,不再运⾏此线程,当定时时间结束再取回cpu资源,
  2. 参与cpu的调度,获取到cpu资源后就可以继续运⾏了。⽽如果sleep时该线程有锁,那么sleep不会
  3. 释放这个锁,⽽是把锁带着进⼊了冻结状态,也就是说其他需要这个锁的线程根本不可能获取到这个
  4. 锁。也就是说⽆法执⾏程序。如果在睡眠期间其他线程调⽤了这个线程的interrupt⽅法,那么这个
  5. 线程也会抛出interruptexception异常返回,这点和wait是⼀样的。
3. sleep⽅法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。
4. sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别⼈中断)。
5. sleep 一般⽤于当前线程休眠,或者轮循暂停操作,wait 则多⽤于多线程之间的通信。
6. sleep 会让出 CPU 执⾏时间且强制上下文切换,而wait 则不⼀定,wait 后可能还是有机会重新竞争到锁继续执⾏的。 sleep()、wait()、join()、yield()之间的的区别。
7. yield()执行后线程直接进⼊就绪状态,马上释放了cpu的执行权,但是依然保留了cpu的执⾏资格,所以有可能cpu下次进⾏线程调度还会让这个线程获取到执⾏权继续执行。
8. join()执⾏后线程进⼊阻塞状态,例如在线程B中调用线程A的join(),那线程B会进⼊到阻塞队列,直到线程A结束或中断线程。
  1. public static void main(String[] args) throws InterruptedException {
  2. Thread t1 = new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. try {
  6. Thread.sleep(3000);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. System.out.println("22222222");
  11. }
  12. });
  13. t1.start();
  14. t1.join();
  15. // 这⾏代码必须要等t1全部执⾏完毕,才会执⾏
  16. System.out.println("1111");
  17. }
  18. ###执行结果
  19. 22222222
  20. 1111
3.Thread和Runable的区?
        Thread和Runnable的实质是继承关系,没有可比性。⽆论使⽤Runnable还是Thread,都会new  Thread,然后执行run方法。⽤法上,如果有复杂的线程操作需求,那就选择继承Thread,如果只是简单的执行⼀个任务,那就实现runnable。

4.并发、并行、串行之间的区别?

1. 串⾏在时间上不可能发⽣重叠,前⼀个任务没搞定,下⼀个任务就只能等着。

2. 并⾏在时间上是重叠的,两个任务在同⼀时刻互不干扰的同时执行。

3. 并发允许两个任务彼此干扰。统⼀时间点、只有⼀个任务运行,交替执行。
5.并发的三大特性?

        原子性 :原子性是指在一个操作中cpu不可以在中途暂停然后再调度,即不被中断操作,要不全部执行完成,要 不都不执行。
        可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
        有序性: 虚拟机在进行代码编译时,对于那些改变顺序之后不会对最终结果造成影响的代码,虚拟机不一定会按照我们写的代码的顺序来执行,有可能将他们重排序。
6.Java死锁如何避免?
造成死锁的几个原因:
        1. 一个资源每次只能被一个线程使⽤。
        2. 一个线程在阻塞等待某个资源时,不释放已占有资源。
        3. 一个线程已经获得的资源,在未使用完之前,不能被强行剥夺。
        4. 若干线程形成头尾相接的循环等待资源关系。
这是造成死锁必须要达到的4个条件,如果要避免死锁,只需要不满足其中某一个条件即可。而其中前 3个条件是作为锁要符合的条件,所以要避免死锁就需要打破第4 个条件,不出现循环等待锁的关系。
在开发过程中:
        1. 要注意加锁顺序,保证每个线程按同样的顺序进行加锁。
        2. 要注意加锁时限,可以针对所设置一个超时时间。
        3. 要注意死锁检查,这是一种预防机制,确保在第⼀时间发现死锁并进行解决。
7.为什么用线程池?解释下线程池参数?

用线程池的原因:

1、降低资源消耗;提⾼线程利用率,降低创建和销毁线程的消耗。

2、提⾼响应速度;任务来了,直接有线程可用可执行,而不是先创建线程,再执行。

3 、提⾼线程的可管理性;线程是稀缺资源,使用线程池可以统一分配调优监控。
线程池参数 :
        1.corePoolSize 代表核心线程数,也就是正常情况下创建工作的线程数,这些线程创建后并不会消除,而是一种常驻线程
        2.maxinumPoolSize 代表的是最⼤线程数,它与核心线程数相对应,表示最大允许被创建的线程数,比如当前任务较多,将核心线程数都用完了,还无法满足需求时,此时就会创建新的线程,但是线程池内线程总数不会超过最⼤线程数。
        3.keepAliveTime unit 表示超出核⼼线程数之外的线程的空闲存活时间,也就是核心线程
不会消除,但是超出核心线程数的部分线程如果空闲⼀定的时间则会被消除,我们可以通过
setKeepAliveTime 来设置空闲时间。
        4.workQueue 用 来存放待执行的任务,假设我们现在核心线程都已被使用,还有任务进来则全部 放⼊队列,直到整个队列被放满但任务还再持续进⼊则会开始创建新的线程。
        5.ThreadFactory 实际上是⼀个线程工厂,⽤来⽣产线程执行任务。我们可以选择使⽤默认的创建工厂,产⽣的线程都在同一个组内,拥有相同的优先级,且都不是守护线程。当然我们也可以选 择⾃定义线程工厂,一般我们会根据业务来制定不同的线程工厂。
        6.Handler 任务拒绝策略,有两种情况,第⼀种是当我们调⽤ shutdown 等⽅法关闭线程池
后,这时候即使线程池内部还有没执行完的任务正在执行,但是由于线程池已经关闭,我们再继续
想线程池提交任务就会遭到拒绝。另⼀种情况就是当达到最⼤线程数,线程池已经没有能⼒继续处
理新提交的任务时,这是也就拒绝。
8.线程池的底层⼯作原理
线程池内部是通过队列+线程实现的,当我们利用线程池执行任务时:
       
         1. 如果此时线程池中的线程数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
       
         2. 如果此时线程池中的线程数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
         
        3. 如果此时线程池中的线程数量⼤于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量⼩于maximumPoolSize,建新的线程来处理被添加的任务。
        
        4. 如果此时线程池中的线程数量⼤于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
        
        5. 当线程池中的线程数量⼤于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

9.ReentrantLock中tryLock()和lock()⽅法的区别

        1. tryLock()表示尝试加锁,可能加到,也可能加不到,该方法不会阻塞线程,如果加到锁则返回 true,没有加到则返回false。
        
        2. lock()表示阻塞加锁,线程会阻塞直到加到锁,方法也没有返回值。
10.Sychronized和ReentrantLock的区别
        1. sychronized是一个关键字,ReentrantLock是一个类。
        2. sychronized会自动的加锁与释放锁,ReentrantLock需要程序员手动加锁与释放锁。
        3. sychronized的底层是JVM层⾯的锁,ReentrantLock是API层面的锁。
        4. sychronized是非公平锁,ReentrantLock可以选择公平锁或非公平锁。
        5. sychronized锁的是对象,锁信息保存在对象头中,ReentrantLock通过代码中int类型的state标识来标识锁的状态。
        6. sychronized底层有一个锁升级的过程。
11.谈谈你对AQS的理解,AQS如何实现可重入锁?
        1. AQS是⼀个JAVA线程同步的框架。是JDK中很多锁工具的核心实现框架。
        2. 在AQS中,维护了⼀个信号量state和⼀个线程组成的双向链表队列。其中,这个线程队列,就是用来给线程排队的,而state就像是一个红绿灯,用来控制线程排队或者放行的。 在不同的场景下,有不用的意义。
        3. 在可重入锁这个场景下,state就⽤来表示加锁的次数。 0 标识无锁,每加一次锁,state就加 1 。释放锁state就减1
12.ThreadLocal的底层原理
        1. ThreadLocal是Java中所提供的线程本地存储机制,可以利用该机制将数据 缓存在某个线程内部 ,该线程可以在任意时刻、任意方法中获取缓存的数据
        2. ThreadLocal底层是通过 ThreadLocalMap来实现的,每个Thread对象(注意不是ThreadLocal对象)中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值
        3. 如果在线程池中使用ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使用完之后,应该要把设置的key,value,也就是Entry对象进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引⽤指向Entry对象,线程不被回收,Entry对象也就不会被回收,从而出现内存泄漏,解决办法是,在使⽤了ThreadLocal对象之后,手动调用ThreadLocal的remove⽅法,手动清楚Entry对象。
        4. ThreadLocal经典的应⽤场景就是连接管理(一个线程持有一个连接,该连接对象可以在不同的方法之间进行传递,线程之间不共享同一个连接)。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频


如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)


最后还为大家献上一个小程序,里面不仅有海量的面试题,还可以根据自己的级别在线刷题。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/808879
推荐阅读
相关标签
  

闽ICP备14008679号