2、线程

厨子大约 6 分钟Java 高并发原创面试题线程程序厨

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/109148693open in new window