多线程面试

标题

  • 1、 死锁的发生原因和怎么避免
  • 2、讲一下 wait 和 notify 这个为什么要在synchronized 代码块中?

1、 死锁的发生原因和怎么避免

死锁,简单来说就是两个或者两个以上的线程在执行的过程中,争夺同一个共享
资源造成的相互等待的现象。
如果没有外部干预,线程会一直阻塞无法往下执行,这些一直处于相互等待资源
的线程就称为死锁线程。

导致死锁的条件有四个,也就是这四个条件同时满足就会产生死锁。
互斥条件,共享资源 X 和 Y 只能被一个线程占用;
请求和保持条件,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不
释放共享资源 X;
不可抢占条件,其他线程不能强行抢占线程 T1 占有的资源;
跟着Mic学架构 跟着Mic学架构 跟着Mic学架构 跟着Mic学架构 跟着Mic学架构 跟着Mic学架构 跟着Mic学架构 跟着Mic学架构 跟着Mic学架构
循环等待条件,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的
资源,就是循环等待。
导致死锁之后,只能通过人工干预来解决,比如重启服务,或者杀掉某个线程。
所以,只能在写代码的时候,去规避可能出现的死锁问题。
按照死锁发生的四个条件,只需要破坏其中的任何一个,就可以解决,但是,互
斥条件是没办法破坏的,因为这是互斥锁的基本约束,其他三方条件都有办法来
破坏:

  • 对于“请求和保持”这个条件,我们可以一次性申请所有的资源,这样就不存在等
    待了。
  • 对于“不可抢占”这个条件,占用部分资源的线程进一步申请其他资源时,如果申
    请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了。
  • 对于“循环等待”这个条件,可以靠按序申请资源来预防。所谓按序申请,是指资
    源是有线性顺序的,申请的时候可以先申请资源序号小的,再申请资源序号大的,
    这样线性化后自然就不存在循环了。

2、讲一下 wait 和 notify 这个为什么要在synchronized 代码块中?

wait 和 notify 用来实现多线程之间的协调,wait 表示让线程进入到阻塞状态,
notify 表示让阻塞的线程唤醒。
wait 和 notify 必然是成对出现的,如果一个线程被 wait()方法阻塞,那么必然需
要另外一个线程通过 notify()方法来唤醒这个被阻塞的线程,从而实现多线程之
间的通信。
在多线程里面,要实现多个线程之间的通信,除了管道流以外,只能通过共享变
量的方法来实现,也就是线程 t1 修改共享变量 s,线程 t2 获取修改后的共享变
量 s,从而完成数据通信。
但是多线程本身具有并行执行的特性,也就是在同一时刻,多个线程可以同时执
行。在这种情况下,线程 t2 在访问共享变量 s 之前,必须要知道线程 t1 已经修
改过了共享变量 s,否则就需要等待。
同时,线程 t1 修改过了共享变量 S 之后,还需要通知在等待中的线程 t2。
所以要在这种特性下要去实现线程之间的通信,就必须要有一个竞争条件控制线
程在什么条件下等待,什么条件下唤醒。

而 Synchronized 同步关键字就可以实现这样一个互斥条件,也就是在通过共享
变量来实现多个线程通信的场景里面,参与通信的线程必须要竞争到这个共享变
量的锁资源,才有资格对共享变量做修改,修改完成后就释放锁,那么其他的线
程就可以再次来竞争同一个共享变量的锁来获取修改后的数据,从而完成线程之
前的通信。
所以这也是为什么 wait/notify 需要放在 Synchronized 同步代码块中的原因,有
了 Synchronized 同步锁,就可以实现对多个通信线程之间的互斥,实现条件等
待和条件唤醒。

你可能感兴趣的:(#,Java基础面试,面试,java,职场和发展)