三、多线程


三、多线程


1. 并行和并发有什么区别?

  • 并行指多个事件在同一个时刻发生;并发指在某时刻只有一个事件在发生,某个时间段内由于 CPU 交替执行,可以发生多个事件。
  • 并行没有对 CPU 资源的抢占;并发执行的线程需要对 CPU 资源进行抢占。
  • 并行执行的线程之间不存在切换;并发操作系统会根据任务调度系统给线程分配线程的 CPU 执行时间,线程的执行会进行切换。

2. 线程和进程的区别?

  1. 线程(Thread)与进程(Process)
    进程定义的是应用程序与应用程序之间的边界,一个进程就代表一个应用程序。不同的进程之间不能共享代码和数据空间,而同一进程的不同线程可以共享代码和数据空间。

  2. 一个进程可以包括若干个线程,同时创建多个线程来完成某项任务,便是多线程。
    实现线程的两种方式:继承Thread类,实现Runable接口


3. 守护线程是什么?

守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。所有用户线程停止时,进程会停掉所有守护线程,退出程序。


4. 创建线程有哪几种方式?

(1)继承Thread类
(2)实现Runnable接口
(3)实现Callable接口、
(4)线程池


5. 说一下 runnable 和 callable 有什么区别?

Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,支持泛型
Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息

runnable

@SpringBootTest
class TestApplicationTests implements Runnable {
@Override
public void run(){
}
}

callable

@SpringBootTest
class TestApplicationTests implements Callable {
@Override
public Object call() throws Exception{
return null;
}
}

6. 线程有哪些状态?

  • NEW 初始状态,线程被构建,但是还没有调用 start() 方法

  • RUNNABLE 运行状态,Java线程操作系统中的就绪和运行两种状态笼统地称作“运行中”

  • BLOCKED 阻塞状态,表示线程阻塞于锁

  • WAITING 等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)

  • TIME_WAITING 超时等待状态,该状态不同于WAITING,他可以在指定时间自行返回的

  • TERMINATED 终止状态,表示当前线程已经执行完毕


7. sleep() 和 wait() 有什么区别?

sleep( )方法属于Thread类中,导致程序暂停执行指定的时间,让出CPU给其他线程,但是依然处于监控状态,等指定时间到了就自动回复运行状态,不会释放对象锁;

wait( )方法属于Object类中,会放弃对象锁,进入等待此对象的等待锁定池,只有此对象调用notify( )方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。


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

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

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


9. 线程的 run()和 start()有什么区别?

  • 启动一个线程需要调用 Thread 对象的 start() 方法
  • 调用线程的 start() 方法后,线程处于可运行状态,此时它可以由 JVM 调度并执行,这并不意味着线程就会立即运行
  • run() 方法是线程运行时由 JVM 回调的方法,无需手动写代码调用
  • 直接调用线程的 run() 方法,相当于在调用线程里继续调用方法,并未启动一个新的线程

10. 创建线程池有哪几种方式

  • Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;

  • Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;

  • Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;

  • Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;

  • Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;

  • Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)。

  • ThreadPoolExecutor:最原始的创建线程池的方式,它包含了 7 个参数可供设置,后面会详细讲。

  • 详细答案大佬原文


11. 线程池都有哪些状态?

  • RUNNING :能接受新提交的任务,并且也能处理阻塞队列中的任务;
  • SHUTDOWN:关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务。在线程池处于 RUNNING 状态时,调用 shutdown()方法会使线程池进入到该状态。(finalize() 方法在执行过程中也会调用shutdown()方法进入该状态);
  • STOP:不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程。在线程池处于 RUNNING 或 SHUTDOWN 状态时,调用 shutdownNow() 方法会使线程池进入到该状态;
  • TIDYING:如果所有的任务都已终止了,workerCount (有效线程数) 为0,线程池进入该状态后会调用 terminated() 方法进入TERMINATED 状态。
  • TERMINATED:在terminated() 方法执行完后进入该状态,默认terminated()方法中什么也没有做。

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

  • execute() 参数 Runnable ;submit() 参数 (Runnable) 或 (Runnable 和 结果 T) 或 (Callable)
  • execute() 没有返回值;而 submit() 有返回值
  • submit() 的返回值 Future 调用get方法时,可以捕获处理异常

13. 在 java 程序中怎么保证多线程的运行安全?

  1. 线程的安全性问题体现在
    原子性:一个或者多个操作在 CPU 执行的过程中不被中断的特性
    可见性:一个线程对共享变量的修改,另外一个线程能够立刻看到
    有序性:程序执行的顺序按照代码的先后顺序执行
  2. 解决办法
    JDK Atomic开头的原子类、synchronized、LOCK,可以解决原子性问题
    synchronized、volatile、LOCK,可以解决可见性问题
    Happens-Before 规则可以解决有序性问题

14. 多线程锁的升级原理是什么?

JVM优化synchronized的运行机制,当JVM检测到不同的竞争状态时,就会根据需要自动切换到合适的锁,这种切换就是锁的升级。升级是不可逆的,也就是说只能从低到高,也就是偏向–>轻量级–>重量级,不能够降级


15. 什么是死锁?

线程死锁是指由于两个或者多个线程互相持有所需要的资源,导致这些线程一直处于等待其他线程释放资源的状态,无法继续执行,如果线程都不主动释放所占有的资源,将产生死锁


16. 怎么防止死锁?

  • 死锁的四个必要条件
  1. 互斥条件:线程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个线程所占有。此时若有其他线程请求该资源,则请求线程只能等待。
  2. 不剥夺条件:线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走,即只能由获得该资源的线程自己主动释放。
  3. 请求和保持条件:线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求线程被阻塞,但对自己已获得的资源保持不放。
  4. 循环等待条件:存在一种线程资源的循环等待链,链中每一个线程已获得的资源同时被链中下一个线程所请求。即存在一个处于等待状态的线程集合{Pl, P2, …, pn},其中Pi等待的资源被P(i+1)占有(i=0, 1, …, n-1),Pn等待的资源被P0占有
  • 避免死锁的方式
    加锁顺序(线程按照一定的顺序加锁)
    加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
    死锁检测

17. ThreadLocal 是什么?有哪些使用场景?

ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。通过这种方式,避免资源在多线程间共享。

经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题。


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

同步代码块是通过 monitorenter 和 monitorexit 指令获取线程的执行权
同步方法通过加 ACC_SYNCHRONIZED 标识实现线程的执行权的控制


19. synchronized 和 volatile 的区别是什么?

  • 作用:
    synchronized 表示只有一个线程可以获取作用对象的锁,执行代码,阻塞其他线程。
    volatile 表示变量在 CPU 的寄存器中是不确定的,必须从主存中读取。保证多线程环境下变量的可见性;禁止指令重排序。
  • 区别:
    synchronized 可以作用于变量、方法、对象;volatile 只能作用于变量。
    synchronized 可以保证线程间的有序性、原子性和可见性;volatile 只保证了可见性和有序性,无法保证原子性。
    synchronized 线程阻塞,volatile 线程不阻塞。

20. synchronized 和 Lock 有什么区别?

  1. 实现层面不一样。synchronized 是 Java 关键字,JVM层面 实现加锁和释放锁;Lock 是一个接口,在代码层面实现加锁和释放锁
  2. 是否自动释放锁。synchronized 在线程代码执行完或出现异常时自动释放锁;Lock 不会自动释放锁,需要再 finally {} 代码块显式地中释放锁
  3. 是否一直等待。synchronized 会导致线程拿不到锁一直等待;Lock 可以设置尝试获取锁或者获取锁失败一定时间超时
  4. 获取锁成功是否可知。synchronized 无法得知是否获取锁成功;Lock 可以通过 tryLock 获得加锁是否成功
  5. 功能复杂性。synchronized 加锁可重入、不可中断、非公平;Lock 可重入、可判断、可公平和不公平、细分读写锁提高效率

21. synchronized 和 ReentrantLock 区别是什么?

  1. synchronized 竞争锁时会一直等待;ReentrantLock 可以尝试获取锁,并得到获取结果
  2. synchronized 获取锁无法设置超时;ReentrantLock 可以设置获取锁的超时时间
  3. synchronized 无法实现公平锁;ReentrantLock 可以满足公平锁,即先等待先获取到锁
  4. synchronized 控制等待和唤醒需要结合加锁对象的 wait() 和 notify()、notifyAll();ReentrantLock 控制等待和唤醒需要结合 Condition 的 await() 和 signal()、signalAll() 方法
  5. synchronized 是 JVM 层面实现的;ReentrantLock 是 JDK 代码层面实现
  6. synchronized 在加锁代码块执行完或者出现异常,自动释放锁;ReentrantLock 不会自动释放锁,需要在 finally{} 代码块显示释放

补充一个相同点:都可以做到同一线程,同一把锁,可重入代码块。


22. 说一下 atomic 的原理?

  • atomic是通过CAS原理解决并发情况下原子性问题;
    CAS 包含 3 个参数,CAS(V, E, N)。V 表示需要更新的变量,E 表示变量当前期望值,N 表示更新为的值。只有当变量 V 的值等于 E 时,变量 V 的值才会被更新为 N。如果变量 V 的值不等于 E ,说明变量 V 的值已经被更新过,当前线程什么也不做,返回更新失败。
  • 当多个线程同时使用 CAS 更新一个变量时,只有一个线程可以更新成功,其他都失败。失败的线程不会被挂起,可以继续重试 CAS,也可以放弃操作。
  • 通过降低锁粒度(多段锁)增加并发性能。从而避免synchronized 的高开销,执行效率大为提升。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无痕YF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值