Java中一個線程只有六個狀態。至於阻塞、可運行、掛起狀態都是人們爲了便於理解,本身加上去的。

java中,線程的狀態使用一個枚舉類型來描述的。這個枚舉一共有6個值: NEW(新建)、RUNNABLE(運行)、BLOCKED(鎖池)、TIMED_WAITING(定時等待)、WAITING(等待)、TERMINATED(終止、結束)。html

可是我發現大多數人的理解和上面的這六種仍是有些差異,一般會加上阻塞狀態,可運行狀態,掛起狀態。java

這是Thread類描述線程狀態的枚舉類的源代碼:ide

 public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

1、大多數人對線程狀態以及狀態轉換的理解this

線程的狀態轉換:spa

  當一個線程建立之後,就處於新建狀態。那何時這個狀態會改變呢?只要它調用的start()方法,線程就進入了鎖池狀態操作系統

  進入鎖池之後就會參與鎖的競爭,當它得到鎖之後還不能立刻運行,由於一個單核CPU在某一時刻,只能執行一個線程,因此他須要操做系統分配給它時間片,才能執行。因此人們一般把一個線程在得到鎖後,得到系統時間片以前的狀態稱之爲可運行狀態,但Java源代碼裏面並無可運行狀態這一說。線程

  當一個持有對象鎖的線程得到CPU時間片之後,開始執行這個線程,此時叫作運行狀態3d

  當一個線程正常執行完,那麼就進入終止(死亡)狀態。系統就會回收這個線程佔用的資源。code

  可是,線程的執行並非那麼順利的。一個正在運行的線程有可能會進入I/O交互,還可能調用sleep()方法,還有可能在當前線程當中有其它線程調用了join()方法。這時候線程就進入了阻塞狀態(但這也只是人們在理解的時候意淫加上去的,源代碼裏也沒有定義這一個狀態)。阻塞狀態的線程是沒有釋放對象鎖的。當I/O交互完成,或sleep()方法完成,或其它調用join()方法的線程執行完畢。阻塞狀態的線程就會恢復到可運行狀態,此時若是再次得到CPU時間片就會進入運行狀態。orm

  一個處於運行狀態的線程還可能調用wait()方法、或者帶時間參數的wait(long milli)方法。這時候線程就會將對象鎖釋放,進入等待隊列裏面(若是是調用wait()方法則進入等待狀態,若是是調用帶時間參數的則進入定時等待狀態)

  一個線程若是的調用了帶時間參數的wait(long milli)方法進入了定時等待狀態,那麼只要時間一到就會進入鎖池狀態,並不須要notify()或notifyAll()方法來喚醒它。若是調用的是不帶時間參數的wait()則須要notify()或notifyAll()這兩個方法來喚醒它而後進入鎖池狀態。進入鎖池狀態之後繼續參與鎖的競爭。

  當一個處於運行狀態的線程調用了suspend()方法之後,它就會進入掛起狀態(這一方法已通過時不建議使用)。掛起狀態的線程也沒有釋放對象鎖,它須要調用resume()方法之後才能恢復到可運行狀態。將線程掛起容易致使程序死鎖。

下面是我本身畫線程狀態轉換圖:

這是大多數對一個線程的狀態的理解。我之前也是這麼理解的,可是,如今我對線程的狀態有了新的理解。這與大多數人的理解有些不同。

2、我對線程狀態以及轉換的理解

下面是我對線程的本身的理解,若是有不對的地方但願懂的人能夠指出來一塊兒討論一下。

據官方源碼,一個線程有六個狀態,沒有阻塞狀態,沒有可運行,沒有掛起狀態。

因此,如今我要提出一個觀點:一個處於等待狀態、定時等待狀態的線程它也可能持有對象鎖。例如調用sleep(long millis)會使線程進入等待狀態,可是沒有釋放鎖。

線程沒有阻塞狀態,那一個正在運行的線程進入I/O,或調用sleep()方法,或當前線程當中有其它線程調用了join()方法時,線程就會進入什麼狀態呢,它總得有六個當中的一個吧。

①、當線程調用sleep()方法或當前線程中有其餘線程調用了帶時間參數的join()方法的時候進入了定時等待狀態(TIMED_WAITING)。

 

②、當其餘線程調用了不帶時間參數的join()方法時進入等待狀態(WAITING)。

 

③、當線程遇到I/O的時候仍是運行狀態(RUNNABLE)。

④、當一個線程調用了suspend()方法掛起的時候它仍是運行狀態(RUNNABLE)。

如今我要來證實一下以上四點,若是證實過程有誤,但願可以獲得指正。這些代碼的能夠直接複製來運行一下。

證實一:當線程調用sleep()方法的時候進入了定時等待狀態。

如今兩個線程t一、t2,t1持有t2的一個引用。啓動兩個線程,t2啓動後當即睡眠,讓t1打印t2的狀態。這樣就能夠看到睡眠時候的線程是六個當中的哪個狀態了。

public class Test1
{
    public static void main(String[] args)
    {
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        t1.setThread2(t2);
        
        t1.start();
        t2.start();
    }
}

//Thread1負責打印兩個線程的狀態。
class Thread1 extends Thread
{
    private Thread2 t2;
    
    public void setThread2(Thread2 t2)
    {
        this.t2 = t2;
    }
    
    @Override
    public void run()
    {
        System.out.println("進入t1線程");
        for(int i = 0; i < 5; i++)
        {
            try
            {
                System.out.println("t1 的狀態: " + getState());
                System.out.println("t2 的狀態: " + t2.getState());
                System.out.println();
                
                //爲了減小打印次數,因此t1每打印一次睡1秒
                Thread.sleep(1000);
            } catch (InterruptedException e)
            {
                
            }
        }
    }
}

class Thread2 extends Thread
{
    @Override
    public void run()
    {
        System.out.println("進入t2線程,立刻進入睡眠");
        try
        {
            //睡眠5秒鐘。
            sleep(5000);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        System.out.println("t2睡眠結束");
    }
}

上面的程序執行打印的結果

說明調用sleep()方法之後線程處於定時等待狀態(TIMED_WAITING)至於網上一直說的處於等待狀態的線程不持有對象鎖這種說法,我不知道這是官方給出的仍是人們本身定義的。

 證實二:當其餘線程調用了join()方法時進入等待狀態。

如今有三個線程t1 、t2 、t3。t1負責打印三個線程的狀態。t2線程持有t3線程的引用,當進入t2線程之後,當即啓動t3線程,並調用t3.join()方法。當t3加入後t2就是等待狀態。

public class Test1
{
    public static void main(String[] args)
    {
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        Thread3 t3 = new Thread3();
        
        //t1須要持有t2,t3的引用,以便打印他們的狀態。
        t1.setThread2(t2,t3);
        
        //t2須要持有t3的引用,以便t3可以在t2執行時加入(調用join()方法)
        t2.setTh3(t3);
        
        t1.start();
        t2.start();
    }
}

//Thread1負責打印全部線程的狀態。
class Thread1 extends Thread
{
    private Thread2 t2;
    private Thread3 t3;
    
    public void setThread2(Thread2 t2, Thread3 t3)
    {
        this.t2 = t2;
        this.t3 = t3;
    }
    
    @Override
    public void run()
    {
        System.out.println("進入t1線程");
        for(int i = 0; i < 5; i++)
        {
            try
            {
                System.out.println("t1 的狀態: " + getState());
                System.out.println("t2 的狀態: " + t2.getState());
                System.out.println("t3 的狀態: " + t3.getState());
                System.out.println();
                
                //爲了減小打印次數,因此t1每打印一次睡1秒
                Thread.sleep(1000);
            } catch (InterruptedException e)
            {
                
            }
        }
    }
}


class Thread2 extends Thread
{
    private Thread3 t3;
    
    public void setTh3(Thread3 t3)
    {
        this.t3 = t3;
    }
    
    //當進入t2線程之後立刻啓動t3線程並調用join()方法。
    @Override
    public void run()
    {
        System.out.println("進入t2線程,t3準備加入(調用join()方法)");
        t3.start();
        try
        {
            t3.join();
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        System.out.println("t2執行結束");
    }
}
class Thread3 extends Thread
{
    @Override
    public void run()
    {
        System.out.println("進入t3線程,準備睡眠");
        
        //原本是想讓t3線程作加法運算的,奈何電腦算太快了,因此改成睡眠。由於睡眠不釋放鎖,因此效果同樣。
        try
        {
            sleep(5000);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        
        System.out.println("t3線程結束");
    }
}

這是運行的打印結果,當t3加入後t2處於等待狀態

 根據結果看,說明當一個正在執行的線程在其餘線程調用join()方法之後進入了等待狀態。

 

 證實三:當一個線程調用了suspend()方法的時候它仍是運行狀態(RUNNABLE)。

package com.zcd.observe;

public class Test1
{
    public static void main(String[] args)
    {
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        
        //t1須要持有t2,以便打印狀態,和控制它恢復運行。
        t1.setThread2(t2);
        
        t1.start();
        t2.start();
    }
}

//Thread1負責打印全部線程的狀態。
class Thread1 extends Thread
{
    private Thread2 t2;
    
    public void setThread2(Thread2 t2)
    {
        this.t2 = t2;
    }
    
    @Override
    public void run()
    {
        System.out.println("進入t1線程");
        for(int i = 0; i < 6; i++)
        {
            try
            {
                System.out.println("t1 的狀態: " + getState());
                System.out.println("t2 的狀態: " + t2.getState());
                System.out.println();
                
                if(i == 3)
                {
                    //恢復t2的運行。
                    t2.resume();
                }
                
                //爲了減小打印次數,因此t1每打印一次睡1秒
                Thread.sleep(1000);
            } catch (InterruptedException e)
            {
                
            }
        }
        
    }
}

class Thread2 extends Thread
{
    @Override
    public void run()
    {
        System.out.println("進入t2線程,掛起");
        //將線程掛起。讓t1來控制它的恢復運行。
        suspend();
        
        System.out.println("t2已經恢復運行");
        System.out.println("t2正在打印1");
        System.out.println("t2正在打印2");
        System.out.println("t2正在打印3");
        
        
        System.out.println("t2線程結束");
    }
}

執行結果截圖

說明:當一個線程調用了suspend()方法的時候它仍是運行狀態(RUNNABLE)。

證實四:當線程遇到I/O的時候仍是運行狀態。

package com.zcd.observe;

import java.util.Scanner;

public class Test1
{
    public static void main(String[] args)
    {
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        
        t1.setThread2(t2);
        
        t1.start();
        t2.start();
    }
}

//Thread1負責打印全部線程的狀態。
class Thread1 extends Thread
{
    private Thread2 t2;
    
    public void setThread2(Thread2 t2)
    {
        this.t2 = t2;
    }
    
    @Override
    public void run()
    {
        System.out.println("進入t1線程");
        for(int i = 0; i < 6; i++)
        {
            try
            {
                System.out.println("t1 的狀態: " + getState());
                System.out.println("t2 的狀態: " + t2.getState());
                System.out.println();
                
                
                //爲了減小打印次數,因此t1每打印一次睡1秒
                Thread.sleep(1000);
            } catch (InterruptedException e)
            {
                
            }
        }
        System.out.println("進入t1線程結束");
    }
}

class Thread2 extends Thread
{
    @Override
    public void run()
    {
        System.out.println("進入t2線程");
        
        //讓線程進入I/O
        System.out.println("請輸入數據:");
        Scanner scan = new Scanner(System.in);
        String read = scan.nextLine();
        System.out.println("您輸入的數據爲:"+read); 
        
        System.out.println("t2線程結束");
    }
}

執行結果截圖:

說明:當線程遇到I/O的時候仍是運行狀態。

 下面在附上我畫的狀態轉換圖,一共只有六個狀態。

 

 

   我以爲以上兩種理解方式只是站在同的角度去理解而已。

本文爲做者原創,轉載須要聲明而且附上本文地址:http://www.cnblogs.com/GooPolaris/p/8079490.html 

相關文章
相關標籤/搜索