2、线程
2.线程
2.0 多线程的优缺点
优点:当一个线程进入等待状态或者阻塞时,CPU可以先去执行其他线程,提高CPU的利用率。
缺点:
资源限制:因为计算机资源有限,所以有可能让我们想要并发运行的部分,依旧是串行,这样就程序就会执行很慢,因为程序并发需要上下文切换和资源调度。
上下文切换:因为有可能造成上下文切换。
死锁:有可能因为竞争出现死锁
2.1 线程的上下文切换
因为我们线程是调度的基本单位,但是线程共用进程的虚拟地址空间,所以线程调度速度更快。
单核的CPU也是支持多线程,多是通过分配时间片来进行线程的调度,时间片是每个线程的执行时间,所以CPU需要不断的切换线程。
CPU会通过时间片分配算法来循环执行任务,当前任务执行完一个时间片后会切换到下一个任务,但切换前会保存上一个任务的状态,因为下次切换回这个任务时还要加载这个任务的状态继续执行,从任务保存到再加载的过程就是一次上下文切换。
2.2 守护线程和用户线程的区别?
我们可以理解成守护线程是服务与用户线程的,保证用户线程的平稳运行,我们可以通过SetDaemon来设置成守护线程,不过守护线程要设置成 start()之前。不然会抛出异常,比如我们的垃圾回收线程就是用户线程。
用户线程:平常使用到的线程均为用户线程
守护线程:垃圾回收线程
主要区别就是JVM虚拟机是否存活
用户线程:当任何一个用户线程未结束,Java虚拟机是不会结束的。
守护线程:如何只剩守护线程未结束,Java虚拟机结束。
2.3 死锁,活锁和饥饿有什么区别
死锁是因为线程竞争资源而产生的,请求和保持,互斥,不可剥夺,等待。
活锁:线程间互相礼让,同时让其他线程先获取,然后造成都无法获取。
死锁和活锁的区别:
活锁是在不断地尝试、死锁是在一直等待。
活锁有可能自行解开、死锁无法自行解开
饥饿:
饥饿就是某些线程一直得不到资源,进行运行,比如我们的使用的短作业优先的调度方式的话,则有可能造成长作业一直得不到运行,如果一直有短作业来的话。
另外优先级调度的情况,也有可能造成饥饿的情况。
产生饥饿的原因:
高优先级的线程占用了低优先级线程的CPU时间
线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。
线程在等待一个本身也处于永久等待完成的对象(比如调用这个对象的
wait()
方法),因为其他线程总是被持续地获得唤醒。
2.4 线程同步和线程调度相关的方法有哪些?
wait方法
:使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
sleep方法:使一个线程暂停执行,但是不会释放锁。
notify():唤醒,wait线程,但是不确定是哪一个,由JVM控制。
notifyall():唤醒所有线程
interrupt():发送中断命令,告知其有人希望你中断执行,具体如何执行还是要看你自己。我们可以根据中断标识位进行修改。
join(): 与sleep()
方法一样,是一个可中断的方法,在一个线程中调用另一个线程的join()
方法,会使得当前的线程挂起,知直到执行join()
方法的线程结束。例如在B线程中调用A线程的join()
方法,B线程进入阻塞状态,直到A线程结束或者到达指定的时间。
yield():提醒调度器,该线程愿意放弃CPU资源,从运行态,到就绪态。
2.5 sleep和yield的异同
sleep仅仅是暂停住,暂停指定的时间,并且不会释放锁,没有消耗时间片。
yield()
只是对CPU进行提示,如果CPU没有忽略这个提示,会使得线程上下文的切换,进入到就绪状态。
sleep一定会执行成功暂停指定的时间,yeild不一定。
2.6 sleep 和 wait 的异同
相同点:
sleep和wait都会发生状态的转化,转换成阻塞状态。
不同点:
wait()是Object方法,sleep是Thread方法
sleep不会释放锁,而wait会释放锁
sleep到相应时间之后,会退出堵塞,但是wait需要其他线程唤醒才可以。
sleep可以通过interrupt进行停止。
2.7 如何唤醒一个阻塞的线程
我们需要具体情况,具体分析
wait:
我们可以使用 notify 和 notifyall 来进行唤醒
sleep():
调用该方法,会使其在指定状态进行进入阻塞状态,等到指定时间过去,线程再次获取到 CPU 时间片而被再次唤醒。
join() :
当前线程A调用另一个线程B的 join() 方法,当线程B运行完毕之后,线程则会从阻塞转为可执行状态。
yield():
使得当前线程放弃CPU时间片,但随时可能再次得到时间片而激活。
2.8 如何实现两个线程之间的通信和协作?
我们可以使用wait的notify / notifyAll
来进行。
ReentrantLock类加锁的线程的Condition类的await和 signal/signAll()
来实现
2.9 什么是线程同步,什么是线程互斥,他们如何实现。
我们可以通过wait 和 notify来实现
volatile实现同步
同步方法
整体含义就是让保证线程间的通信。
2.10 在Java中有那些方法可以保证线程安全
线程安全主要是,原子性,可见性,有序性
原子性:Atomic、synchronized、Lock来实现。
可见性:volatile、synhronized、Lock
有序性:Happens-Before
推荐阅读:https://blog.csdn.net/qq_32273417/article/details/109148693
