线程的六种状态

  • 新建(New)

  • 可运行状态(Runnable):线程对象创建后,其他线程(比如 main 线程)调用了该对象的 start ()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取CPU的使用权;

  • 正在运行状态(Running):线程获得了CPU时间片( timeslice ) ,执行程序代码;

  • 阻塞状态(Block):阻塞状态是指线程因为某种原因放弃了CPU 使用权,也即让出了 CPU timeslice ,暂时停止运行。直到线程进入可运行( runnable )状态,才有 机会再次获得 cpu timeslice 转到运行( running )状态。
    1)等待阻塞:运行( running )的线程执行 o . wait ()方法, JVM 会把该线程放 入等待队列( waitting queue )中。
    2)同步阻塞:运行( running )的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池( lock pool )中。
    3)其他阻塞: 运行( running )的线程执行 Thread . sleep ( long ms )或 t . join ()方法,或者发出了 I / O 请求时, JVM 会把该线程置为阻塞状态。当 sleep ()状态超时、 join ()等待线程终止或者超时、或者 I / O 处理完毕时,线程重新转入可运行( runnable )状态。

  • 终止(Dead):线程 run ()、 main () 方法执行结束,或者因异常退出了 run ()方法,则该线程结束生命周期。死亡的线程不可再次复生。

线程的生命周期

注意:stop()方法已被淘汰,调用stop()方法抛出以下异常:

java.lang.UnsupportedOperationException

stop() 与 suspend()被弃用的原因

  • stop这个方法将终止所有未结束的方法,包括run方法。当一个线程停止时候,他会立即释放所有他锁住对象上的锁。这会导致对象处于不一致的状态。假如一个方法在将钱从一个账户转移到另一个账户的过程中,在取款之后存款之前就停止了。那么现在银行对象就被破坏了。因为锁已经被释放了。当线程想终止另一个线程的时候,它无法知道何时调用stop是安全的,何时会导致对象被破坏。所以这个方法被弃用了。你应该中断一个线程而不是停止他。
  • suspend被弃用的原因是因为它会造成死锁。suspend方法和stop方法不一样,它不会破换对象和强制释放锁,相反它会一直保持对锁的占有,一直到其他的线程调用resume方法,它才能继续向下执行。假如有A,B两个线程,A线程在获得某个锁之后被suspend阻塞,这时A不能继续执行,线程B在或者相同的锁之后才能调用resume方法将A唤醒,但是此时的锁被A占有,B不能继续执行,也就不能及时的唤醒A,此时A,B两个线程都不能继续向下执行而形成了死锁。
  • 如何合理地中断线程呢?

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
    public class TempThread extends Thread {  
     public volatile boolean flag = false;   
         public void run() {   
         while (!flag){  
             //do something  
         }  
     }   
    } 
  2. 使用interrupt()方法中断线程
    注意:并不是只要调用interrupt方法线程就会结束,一定要先捕InterruptedException异常之后通过break或者return来跳出循环,才能正常结束run方法。
    public class ThreadSafe extends Thread {  
     public void run() {   
         while (true){  
             try{  
                     Thread.sleep(5*1000);阻塞5妙  
                 }catch(InterruptedException e){  
                     e.printStackTrace();  
                     break;//捕获到异常之后,执行break跳出循环。  
                 }  
         }  
     }   
    } 
  3. 线程未进入阻塞状态,使用isInterrupted()判断线程的中断标志来退出循环,当使用interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。
public class ThreadSafe extends Thread {
    public void run() { 
        while (!isInterrupted()){
            //do something, but no tthrow InterruptedException
        }
    } 
}

join()方法

public final void join()
作用:等待调用该方法的线程结束后才能执行
public final void join(long millis)
作用:等待该线程终止的最长时间为millis毫秒
注意:
1) 只有静态方法才能通过类名直接访问,join方法不是静态的
2) join方法是最终方法,不能被重写
3) 调用join方法可以使其他线程由正在运行状态编为阻塞状态

什么是死锁(deadlock)?

两个线程或两个以上线程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是这些线程都陷入了无限的等待中。

如何确保N个线程可以访问N个资源同时又不导致死锁?

指定获取锁的顺序,并强制线程按照指定的顺序获取锁。
因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。

多线程产生死锁的四个必要条件:

  • 互斥条件:一个资源每次只能被一个进程使用。
  • 保持和请求条件:一个进程因请求资源而阻塞时,对已获得资源保持不放。
  • 不可剥夺性:进程已获得资源,在未使用完成前,不能被剥夺。
  • 循环等待条件(闭环):若干进程之间形成一种头尾相接的循环等待资源关系。
    只要破坏其中任意一个条件,就可以避免死锁

图片说明