JavaSE高频面试题梳理-2.多线程与并发

jupiter
2022-09-07 / 0 评论 / 317 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2022年09月07日,已超过596天没有更新,若内容或图片失效,请留言反馈。

0.题目汇总

  1. Java中实现多线程有几种方法
  2. 如何停止一个正在运行的线程
  3. notify()和notifyAll()有什么区别?
  4. sleep()和wait()有什么区别
  5. volatile是什么?可以保证有序性吗?
  6. Thread 类中的start()和run()方法有什么区别?
  7. 为什么wait, notify 和notifyAll这些方法不在thread类里面?
  8. 为什么wait, notify方法要在同步块中调用?
  9. Java中interrupted和isInterruptedd方法的区别
  10. Java中synchronized和ReentrantLock有什么不同?
  11. 有三个线程T1, T2, T3,如何保证顺序执行?
  12. SynchronizedMap 和 ConcurrentHashMap 有什么区别?
  13. Thread类中的yield方法有什么作用?
  14. Java线程池中submit()和execute()方法有什么区别?
  15. volatile关键字的作用?
  16. 常用的线程池有哪些?
  17. 简述一下你对线程池的理解
  18. Runnable接口和Callable接口的区别

1.Java中实现多线程有几种方法

  1. 继承 Thread 类
  2. 实现 Runnable 接口
  3. 实现 Callable 接口:Runnable接口是没有返回值的,Callable有返回值,可以抛出异常;Thread类并不接受Callable对象。可以使用FutureTask类实现Runnable接口和Future接口;

    线程池

线程池的概念:

线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。

使用线程池的原因:

多线程运行时间,系统不断的启动和关闭新线程,成本非常高,会过渡消耗系统资源,以及过渡切换线程的危险,从而可能导致系统资源的崩溃。这时,线程池就是最好的选择了。

2.如何停止一个正在运行的线程

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
  2. 使用stop方法强行终止,但是不推荐这个方法,因为这个方法是不安全的,而且是已被废弃的方法。
  3. 使用interrupt方法中断线程

3.notify()和notifyAll()有什么区别?

二者都是用来用来唤醒调用wait()方法进入等待锁资源队列的线程,区别在于:

  • notify():

唤醒正在等待此对象监视器的单个线程。 如果有多个线程在等待,则选择其中一个随机唤醒(由调度器决定),唤醒的线程享有公平竞争资源的权利

  • notifyAll():

唤醒正在等待此对象监视器的所有线程,唤醒的所有线程公平竞争资源

4.sleep()和wait()有什么区别

sleep()和wait()都是线程暂停执行的方法

1、这两个方法来自不同的类分别是Thread和Object,sleep方法属于Thread类中的静态方法,wait属于Object的成员方法。
2、sleep()是线程类(Thread)的方法,不涉及线程通信,调用时会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复wait()是Object的方法,用于线程间的通信,调用时会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才进入对象锁定池准备获得对象锁进入运行状态。
3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)。
4、sleep()方法必须捕获异常InterruptedException,而wait()、notify()和notifyAll()不需要捕获异常。

注意:

  • sleep方法只让出了CPU,而并不会释放同步资源锁。
  • 线程执行sleep()方法后会转入阻塞状态。
  • sleep()方法指定的时间为线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。

5.volatile是什么?可以保证有序性吗?

volatile是java并发编程的一个关键字。volatile可以保证可见性,有序性(一定程度上),但不能保证原子性

因为volatile关键字能禁止指令重排,所以一定程度上保证了有序性
volatile关键字禁止指令重排的两层意思:
(1)当程序执行volatile关键字进行读操作或写操作时,volatile关键字前面所有程序操作已经全部完成且结果对后面的所有操作均显示,volatile关键字后面的操作已经还没有进行
(2)进行指令优化时,不能将volatile关键字后面的语句放在volatile关键字前面执行,也不能将volatile关键字前面的语句放在volatile关键字后面执行

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值

有序性即程序执行的顺序按照代码的先后顺序执行

原子性即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

volatile关键字的应用

  1. 状态标记量
  2. 单例模式中的double check

指令重排序

一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。

比如上面的代码中,语句1和语句2谁先执行对最终的程序结果并没有影响,那么就有可能在执行过程中,语句2先执行而语句1后执行。

但是有依赖关系的语句不会进行重排序,如下面求圆面积的代码

double pi = 4.14   //A
double r = 1.0     //B
double area = pi * r * r   //c 

程序的执行顺序只有下面这2个形式
A->B->C和B->A->C,因为A和C之间存在依赖关系,同时B和C之间也存在依赖关系。因此最终执行的指令序列中C不能被重排序到A和B前面。

6.Thread 类中的start()和run()方法有什么区别?

  • start():

使用start可以启动线程,创建一个新的线程,真正实现了多线程运行,在内部调用run方法且无需等待run方法体代码执行完毕而直接继续执行下面的代码。

  • run():

run()方法只是类的一个普通方法而已,使用run不会创建新线程,而是在之前的线程中执行了我们在run里面重写的内容。

7.为什么wait, notify 和notifyAll这些方法不在thread类里面?

Java提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。简单的说,由于wait,notify,notifyAll都是锁级别的操作,所以把他们定义在object类中因为锁属于对象。

8.为什么wait, notify方法要在同步块中调用?

参考:面试官:为什么wait()方法要放在同步块中?

9.Java中interrupted和isInterruptedd方法的区别?

  • interrupt

interrupt 方法用于中断线程。调用该方法的线程的状态为将被置为”中断”状态。

注意:线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监

视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出

interruptedException 的方法)就是在监视线程的中断状态,一旦线程的中断状

态被置为“中断状态”,就会抛出中断异常

  • interrupted

查询当前线程的中断状态,并且清除原状态。如果一个线程被中断了,第一次调

用 interrupted 则返回 true,第二次和后面的就返回 false 了。

  • isInterrupted

仅仅是查询当前线程的中断状态

10.Java中synchronized和ReentrantLock有什么不同?

  • 用法不同:synchronized 可以用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用于代码块(lock()和unlock()方法配合try/finally语句块来完成)。
  • 获取锁和释放锁的机制不同:synchronized 是自动加锁和释放锁的,而 ReentrantLock 需要手动加锁和释放锁。
  • 锁类型不同:synchronized 是非公平锁,而 ReentrantLock 默认为非公平锁,也可以手动指定为公平锁。
  • 响应中断不同:ReentrantLock 可以响应中断(catch InterruptedException),解决死锁的问题,而 synchronized 不能响应中断。
  • 底层实现不同:synchronized 是 JVM 层面通过监视器实现的,而 ReentrantLock 是基于 AQS 实现的。
公平锁是指多个线程在等待同一个锁时,必须按照申请的时间顺序来依次获得锁;而非公平锁则不能保证这一点。非公平锁在锁被释放时,任何一个等待锁的线程都有机会获得锁。

11.有三个线程T1, T2, T3,如何保证顺序执行?

用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。

12.SynchronizedMap 和 ConcurrentHashMap 有什么区别?

  1. SynchronizedMap 一次锁住整张表来保证线程安全,所以每次只能有一个线程来访问Map.ConcurrentHashMap 使用分段锁来保证在多线程下的性能。
  2. ConcurrentHashMap 中则是一次锁住一个桶。ConcurrentHashMap 默认将 hash 表分为 16 个桶,诸如get、put、remove 等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有 16 个写线程执行,并发性能的提升是显而易见的。
  3. 另外 ConcurrrentHashMap 使用一种不同的迭代方式。在这种迭代方式中,当 iterator 被创建后集合再发生改变就不再是抛出 ConcurrentModificationException,取而代之的是在改变 new 新的数据从而不影响原有的数据,iterator 完成后再将头指针替换为新的数据,这样 iterator 线程可以使用原来老的数据,而写线程也可以并发的完成改变。

13.Thread类中的yield方法有什么作用?

yield 即 "谦让",也是 Thread 类的方法。它让掉当前线程 CPU 的时间片,使正在运行中的线程重新变成就绪状态,并重新竞争 CPU 的调度权。它可能会获取到,也有可能被其他线程获取到。

yield 和 sleep 的异同

1)yield, sleep 都能暂停当前线程,sleep 可以指定具体休眠的时间,而 yield 则依赖 CPU 的时间片划分。

2)yield, sleep 两个在暂停过程中,如已经持有锁,则都不会释放锁资源。

3)yield 不能被中断,而 sleep 则可以接受中断。

14.Java线程池中submit()和execute()方法有什么区别?

两个方法都可以向线程池提交任务,execute()方法的返回类型是 void,它定义在Executor 接口中。而 submit()方法可以返回持有计算结果的 Future 对象,它定义在ExecutorService 接口中,它扩展了 Executor 接口,其它线程池类像ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 都有这些方法。

15.volatile关键字的作用?

  1. volatile关键字的作用
  2. 保证可见性;
  3. 防止指令重排;

16.常用的线程池有哪些?

在这里插入图片描述

17.简述一下你对线程池的理解

线程池本质上是一种池化技术,而池化技术是一种资源复用的思想。它的核心设计目标,我认为有两个:

  1. 减少线程的频繁创建和销毁带来的性能开销,因为线程创建会涉及到CPU上下文切换、内存分配等工作。
  2. 线程池本身会有参数来控制线程创建的数量,这样就可以避免无休止的创建线程带来的资源利用率过高的问题,起到了资源保护的作用。

18.Runnable接口和Callable接口的区别

Runnable需要实现run()方法
Callable需要实现call()方法
Runnable从jdk1.1开始加入
Callable从jdk1.5开始加入
区别1: 两者最大的区别,实现Callable接口的任务线程能返回执行结果,而实现Runnable接口的任务线程不能返回执行结果
注意点:Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞线程直到获取“将来”的结果,当不调用此方法时,主线程不会阻塞
区别2:Callable接口实现类中run()方法允许将异常向上抛出,也可以直接在内部处理(try…catch); 而Runnable接口实现类中run()方法的异常必须在内部处理掉,不能向上抛出

参考资料

  1. Github上365道Java高频面试复习题,助你拿爆大厂offer
  2. Java 实现多线程的四种方式
  3. 详解Java实现多线程的三种方式
  4. Java四种常用线程池的详细介绍
  5. notify() 和 notifyAll()方法的使用和区别
  6. 并发关键字:volatile如何保证可见性和有序性?
  7. 面试官:为什么wait()方法要放在同步块中?
  8. Java面试突击之synchronized和ReentrantLock有什么区别?
  9. 多线程 Thread.yield 方法到底有什么用?
  10. 5种常用的线程池
  11. 【小明】谈谈你对线程池的理解【建议收藏】
  12. 【Java面试】简述一下你对线程池的理解?
0

评论 (0)

打卡
取消