多线程面试题总结

1 篇文章 0 订阅
1 篇文章 0 订阅

1. 线程挂起 (suspend)和继续执行(resume)(被弃用)

不推荐使用suspend()去挂起线程的原因,是因为suspend()在导致线程暂停的同时,并不会去释放任何锁资源。此时,其他任何线程想要访问被它暂用的锁时,都会被牵连,导致无法正常继续运行(如图所示)。直到对应的线程上进行了resume()操作,被挂起的线程才能继续,从而其他阻塞在相关锁上的线程也可以继续执行。但是,如果resume()操作意外地在suspend()前就执行了,那么被挂起的线程可能很难有机会被继续执行。并且,更严重的是:它所占用的锁不会被释放,因此可能会导致整个系统工作不正常。而且,对于被挂起的线程,从它的线程状态上看,居然还是Runnable,这也会严重影响我们对系统当前的判断。
对同步代码块有要求,对顺序有要求
参考链接:链接1
链接2

2.wait(挂起一个线程)和notify(唤醒线程)

不管同步,要求顺序

3.park(阻塞,等待,挂起)和unpark(唤醒,恢复)

其中park中变量isAbsolute代表传入的time是绝对时间还是相对时间。

unpark函数为线程提供“许可(permit)”,线程调用park函数则等待“许可”。这个有点像信号量,但是这个“许可”是不能叠加的,“许可”是一次性的。可以理解为设置一个变量0,1之间的切换。

如果线程B连续调用了多次次unpark函数,当线程A调用park函数就使用掉这个“许可”,如果线程A第二次调用park,则进入等待状态。

注意,unpark函数可以先于park调用。比如线程B调用unpark函数,给线程A发了一个“许可”,那么当线程A调用park时,它发现已经有“许可”了,那么它会马上再继续运行,也就是不会阻塞。

而如果线程A处于等待许可状态,再次调用park,则会永远等待下去,调用unpark也无法唤醒。

没有顺序,不要放入同步代码块

4.wait、join、sleep、yield的区别

sleep:

sleep 方法是属于 Thread 类中的,sleep 过程中线程不会释放锁,只会阻塞线程,让出cpu给其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态,可中断,sleep 给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会

wait

wait 方法是属于 Object 类中的,wait 过程中线程会释放对象锁,只有当其他线程调用 notify 才能唤醒此线程。wait 使用时必须先获取对象锁,即必须在 synchronized 修饰的代码块中使用,那么相应的 notify 方法同样必须在 synchronized 修饰的代码块中使用,如果没有在synchronized 修饰的代码块中使用时运行时会抛出IllegalMonitorStateException的异常

yield

和 sleep 一样都是 Thread 类的方法,都是暂停当前正在执行的线程对象,不会释放资源锁,和 sleep 不同的是 yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。还有一点和 sleep 不同的是 yield 方法只能使同优先级或更高优先级的线程有执行的机会

join

等待调用join方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之后才能继续运行的场景。例如:主线程创建并启动了子线程,如果子线程中要进行大量耗时运算计算某个数据值,而主线程要取得这个数据值才能运行,这时就要用到 join 方法了

①wait与join 区别:
wait需要唤醒 wait需要用在同步里面
②wait与sleep区别
sleep不去释放锁 wait释放
参考链接:区别

5.synchronized和lock的区别

在这里插入图片描述

锁的概念

JAVA 内置锁

隐性锁:每个JAVA对象可以用作实现同步的内置锁,线程在访问同步代码块时必须先获取该内置锁,在退出和中断的时候需要释放内置锁。Java内置锁通过synchronized关键字使用,使用其修饰方法或者代码块,就能保证方法或者代码块以同步方式执行。有对象锁和类锁(static方法和class上枷锁)区分,两者不冲突可以并行存在。

显性锁:显式锁(ReentrantLock)正式为了解决这些灵活需求而生,ReentrantLock的字面意思是可重入锁,可重入的意思是线程可以同时多次请求同一把锁,而不会自己导致自己死锁。

锁的分类

可重入锁:Synchronized和ReentrantLook都是可重入锁,锁的可重入性标明了锁是针对线程分配方式而不是针对方法。例如调用Synchronized方法A中可以调用Synchronized方法B,而不需要重新申请锁。

读写锁:按照数据库事务隔离特性的类比读写锁,在访问统一个资源(一个文件)的时候,使用读锁来保证多线程可以同步读取资源。ReadWriteLock是一个读写锁,通过readLock()获取读锁,通过writeLock()获取写锁。

可中断锁:可中断是指锁是可以被中断的,Synchronized内置锁是不可中断锁,ReentrantLock可以通过lockInterruptibly方法中断显性锁。例如线程B在等待等待线程A释放锁,但是线程B由于等待时间太久,可以主动中断等待锁。

公平锁:公平锁是指尽量以线程的等待时间先后顺序获取锁,等待时间最久的线程优先获取锁。synchronized隐性锁是非公平锁,它无法保证等待的线程获取锁的顺序,ReentrantLook可以自己控制是否公平锁。

线程池都有哪些状态?

RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。

SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。

STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。

TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。

TERMINATED:terminated()方法结束后,线程池的状态就会变成这个。

死锁是什么,产生死锁的条件。如何避免死锁?

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免: 1)尽量避免使用多个锁,并且只有需要时才持有锁定位死锁,嵌套的 synchronized 或者 lock 非常容易出问题。
2)如果必须使用多个锁,尽量设计好锁的获取顺序
3)使用带超时的方法,为程序带来更多可控性。Object.wait(…) 或者 CountDownLatch.await(…),都支持所谓的 timed_wait。
4)尽量不要几个功能用同一把锁。

线程的状态有哪些 :

状态:在 Java 5 以后,线程状态被明确定义在其公共内部枚举类型 java.lang.Thread.State:
新建(NEW)new,表示线程被创建出来还没真正启动的状态
就绪,运行(RUNNABLE)runnable,表示该线程已经在 JVM 中执行,当然由于执行需要计算资源,它可能是正在运行,也可能还在等待系统分配给它 CPU 片段,在就绪队列里面排队。
在其他一些分析中,会额外区分一种状态 RUNNING,但是从 Java API 的角度,并不能表示出来。
阻塞(BLOCKED)blocked,这个状态和我们前面两讲介绍的同步非常相关,阻塞表示线程在等待 Monitor lock。
等待(WAITING)waiting,表示正在等待其他线程采取某些操作。
计时等待(TIMED_WAIT)timed_wait:其进入条件和等待状态类似,但是调用的是存在超时条件的方法,比如 wait 或 join 等方法的指定超时版本,
终止(TERMINATED)terminated,不管是意外退出还是正常执行结束,线程已经完成使命,终止运行,也有人把这个状态叫作死亡。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次。
底层start()方法是使用C语言写的,调用JVM_startThread,开启子线程,然后调用里面的run方法

线程池会有哪些漏洞/安全问题安全性问题

notify和notifyAll方法的区别

notify只会唤醒等待该锁的其中一个线程。notifyAll:唤醒等待该锁的所有线程。
1)永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。
2)永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。

如何判断线程是否安全?

考虑原子性,可见性,有序性。

1.明确哪些代码是多线程运行的代码,
2.明确共享数据 对共享变量的操作是不是原子操作 , 当某一个线程对共享变量进行修改的时候,对其他线程是可见的
保证原子性的是加锁或者同步, 提供了volatile关键字来保证可见性, synchronized和锁和 volatile都能保证有序性
JVM还通过被称为happens-before原则隐式地保证顺序性。
3.明确多线程运行代码中哪些语句是操作共享数据.

1.该对象是否会被多个线程访问修改 ,是的话是否有加锁操作。
2.注意静态变量. ,由于静态变量是属于该类和该类下所有对象共享,可直接通过类名访问/修改,因此在多线程的环境下.可以断言所有对静态变量的修改都会发生线程安全问题

原文链接:多线程参考链接

参考链接:锁的概念
synchronized与lock的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值