Android 常见面试题——死锁,线程的运行、阻塞和死亡

Android 常见面试题——死锁

死锁的定义

一组相互竞争系统资源或进行通信的进程间的“永久”阻塞。如两个线程相互等待对方释放同步监视器时就会发生死锁。

一旦发生死锁,整个程序不会发生任何异常,不会给出任何提示,只是所有线程处于阻塞状态,无法继续。

死锁的原因

(1)竞争不可抢占性资源

(2)竞争可消耗资源

当系统中供多个进程共享的资源如打印机,公用队列等,其数目不足以满足诸进程的需要时,会引起诸进程对资源的竞争而产生死锁。

(3)进程推进顺序不当

进程在运行过程中,请求和释放资源的顺序不当,也同样会导致产生进程死锁。
 

如何避免死锁

1、在系统中不允许进程在已获得某种资源的情况下,申请其他资源。即要想出一个办法,阻止进程在持有资源的同时申请其他资源。

要求每个进程提出新的资源申请前,释放它所占有的资源。这样,一个进程在需要资源时,须先把它先前占有的资源R释放掉,然后才能提出对S的申请,即使它可能很快又要用到资源R。

2、如果占有某些资源的一个进程进行进一步资源请求被拒绝,则该进程必须释放它最初占有的资源,如果有必要,可再次请求这些资源和另外的资源。

3、如果一个进程请求当前被另一个进程占有的一个资源,则操作系统可以抢占另一个进程,要求它释放资源。只有在任意两个进程的优先级都不相同的条件下,该方法才能预防死锁。

4、将系统中的所有资源统一编号,进程可在任何时刻提出资源申请,但所有申请必须按照资源的编号顺序(升序)提出。这样做就能保证系统不出现死锁。

典型的死锁情形

线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都永远处于挂起状态了。

所以,写程序时应该尽量避免同时获得多个锁。如果一定有必要这么做,则要按先后顺序获得锁。

线程的运行、阻塞和死亡

线程运行

如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态,如果机器只有一个CPU,那么在任何时刻都只有一个线程处于运行状态。当线程数大于处理器数时,依然会存在多个线程在同一个CPU上轮换的现象。

线程阻塞

当一个线程运行后,它不可能一直处于运行状态(除非它执行体足够短,瞬间完成),线程在运行过程中需要被中断,目的是使其他的线程获得执行的机会。

1、线程调用sleep()方法主动放弃所占的处理器资源

2、线程试图调用了一个阻塞式IO方法,在该方法返回之前,该线程阻塞

3、线程试图获得一个同步监视器,但是该同步监视器正被其他线程持有

4、线程在等某个通知(notify)

5、程序调用了线程的suspend()方法将该线程挂起。单证方法容易导致死锁,所以尽量避免使用。

被阻塞的线程会在合适的时候重新进入就绪状态,注意是重新进入就绪状态,不是运行状态。即被阻塞的线程阻塞解除后,必须重新等待线程调度器再次调度它。

解除阻塞

1、调用sleep()方法的线程经过了指定时间

2、线程调用的阻塞式IO方法已经返回

3、线程成功地获取了试图获取的同步监视器

4、线程正在等待某个通知时,其他线程发出了一个通知

5、处于挂起的线程被调用了resume()恢复方法

线程死亡

线程会以如下三种方式结束,结束后就处于死亡状态:

1、run()或call()方法执行完成

2、线程抛出一个未捕获的Exception或者Error

3、直接调用线程的stop()方法结束,但该方法容易导致死锁,不推荐

注:如果要测试某个线程是否已经死亡,可以调用该线程的isAlive()方法,当处于就绪、运行、阻塞三种状态时,返回true;当处于新建、死亡状态,返回false。

猜你喜欢

转载自blog.csdn.net/Nobody_else_/article/details/97416486