死磕Java線程—Thread

深入瞭解Thread常用方法。

這篇博客呢沒什麼技術含量,本來Thread源碼並不在我最近的學習範圍內,只是發現線程池的源碼看太不懂(畢竟還沒怎麼用過),所以想着先看看簡單點的Thread,在這裏呢只列舉出了Thread中常用的方法源碼。

在正式看源碼之前,先來看一段小程序吧

interface MyRunnable{
	void run();
}

class MyThread implements MyRunnable{
	private MyRunnable target;
	@Override
	public void run() {
		if(target!=null) 
			target.run();
	}
	public void start() {
		this.run();
	}
	public MyThread(MyRunnable target) {
		this.target=target;
	}
}

public class Run implements MyRunnable{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("Run");
	}
	public static void main(String[] args) {
		MyThread myThread=new MyThread(new Run());
		myThread.start();
	}
}

程序分析:用過Thread就知道,上面這段程序的目的是:模仿Thread調用start()方法。

沒錯,任何一個Java程序員都會告訴你:創建一個新線程,我們只需要實現Runnable接口,然後實現該接口要求實現的方法run(),最後由Thread實例調用start()方法就可以了。只需要知道在start()方法內部會對run()方法進行調用。事實也是這樣。

源碼分析

1、start()

public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

源碼分析:

當調用start()時,首先會判斷threadStatus—線程狀態,防止重複啓動同一線程

/*
     * Java thread status for tools, default indicates thread 'not yet started'
     */
private volatile int threadStatus;

根據源碼註釋,該值用於記錄線程是否已經被啓動。0表示沒有啓動。默認爲0。若判斷到threadStatus不等於0,則拋出異常IllegalThreadStateException

接下來調用group.add(this);將該線程添加到隊列中,group是一個ThreadGroup對象

void add(Thread t) {
        synchronized (this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            if (threads == null) {
                threads = new Thread[4];
            } else if (nthreads == threads.length) {
                threads = Arrays.copyOf(threads, nthreads * 2);
            }
            threads[nthreads] = t;

            nthreads++;

            nUnstartedThreads--;
        }
    }

ThreadGroup的本質就是一個數組,用於存放創建好的線程數,該數組的默認大小爲4,當線程數超過4時,會調用數組工具類Arrays.copyOf()來爲數組擴容,一次性擴大爲原來的兩倍

接着用變量started來標識線程是否成功創建。調用了start0()來在本地機器中開闢一個線程,若成功創建,則started標識爲true,若創建失敗,將該線程從數組中移除

private native void start0();

start0()是一個本地方法,在本地機器上開闢了一個新線程的同時會調用run()方法執行對應的工作

2、run()

@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

源碼分析:

這個比較簡單了,run()方法是在接口Runnable中定義的,Thread實現了Runnable

public class Thread implements Runnable {...}

target是一個Runnable引用,它的值是通過構造方法中傳進來的,通過該接口對象調用其實現類的run()方法

public Thread(Runnable target) {
        this(null, target, "Thread-" + nextThreadNum(), 0);
    }

3、sleep(),yield()

public static native void sleep(long millis) throws InterruptedException;

public static native void yield();

源碼分析:

這兩個方法都是本地方法,調用兩方法都涉及到一個對象的鎖。常常和wait()一起做比較,爲了更好的理解,先來看線程的狀態有哪些吧。

4、獲得線程的狀態getState()

public enum State {

        NEW,

        RUNNABLE,

        BLOCKED,

        WAITING,

        TIMED_WAITING,

        TERMINATED;
    }

源碼分析:

State是一個枚舉,是Thread的內部類,用來表示線程的狀態。從源碼來看,線程的狀態可以分爲New(新創建),Runnable(可運行),Block(阻塞),Waiting(等待),Timed Waiting(計時等待),Terminated(被終止)六種

要獲得一個線程的狀態可以調用getState(),Thread中getState()的實現

public State getState() {
        // get current thread state
        return jdk.internal.misc.VM.toThreadState(threadStatus);
    }

繼續深入到jdk.internal.misc.VM.toThreadState(threadStatus);該方法位於VM類

public static Thread.State toThreadState(int threadStatus) {
        if ((threadStatus & JVMTI_THREAD_STATE_RUNNABLE) != 0) {
            return RUNNABLE;
        } else if ((threadStatus & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) != 0) {
            return BLOCKED;
        } else if ((threadStatus & JVMTI_THREAD_STATE_WAITING_INDEFINITELY) != 0) {
            return WAITING;
        } else if ((threadStatus & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) != 0) {
            return TIMED_WAITING;
        } else if ((threadStatus & JVMTI_THREAD_STATE_TERMINATED) != 0) {
            return TERMINATED;
        } else if ((threadStatus & JVMTI_THREAD_STATE_ALIVE) == 0) {
            return NEW;
        } else {
            return RUNNABLE;
        }
    }

6、最後再來說說sleep(),yield(),wait()

  • sleep()和yield()都是Thread中的方法
  • wait()是Object中的方法
  • 通過調用sleep(milliseconds)可以使任務進入休眠狀態,在這種情況下,任務在指定的時間內不會運行
  • 通過調用yield()可以告訴線程調用機制,自己已經完成了最重要的任務,接下來可以把CPU時間片段分配給其他線程了
  • 通過調用wait()可以使線程掛起。直到線程得到了notify()或notifyAll()消息,線程才進入就緒狀態
  • 調用sleep()的時候,對象的鎖並沒有被釋放,yield()也屬於這種情況,意味着其他線程仍然不能獲得這個鎖
  • 調用wait()的時候,對象的鎖會被釋放,意味着另一個線程可以獲得這個鎖

關於sleep(),yield(),wait()與線程狀態以及鎖的關係,用一張圖表示

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章