一、線程的定義
Java中的線程,簡單來說,就是一段順序執行的代碼,是CPU執行的最小單位。新建並啓動一個線程,本質都是新建一個Thread或Thread子類的對象,然後調用其start方法,最終執行的代碼塊位於run方法內(start方法被調用後,JVM會在某一刻自動執行run方法),start和run方法如下:
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
run方法中,target是Runnable接口的實例。判斷如果target不爲空,那麼就執行其run方法;否則就什麼都不執行。鑑於這種判斷邏輯,新建一個新的有具體執行代碼的執行線程有如下兩種方式:
1、繼承Thread類
- 聲明一個類繼承Thread類,重寫run方法();
- 調用該類無參構造方法,新建該類實例;
- 調用其start方法
2、實現Runnable接口
(優先使用該種方式,原因:多個線程可以同時使用,達到資源共享;java只支持單繼承,但可以實現多接口)
- 聲明一個類RunnableImpl實現Runnable接口,重寫run方法;
- 調用Thread類有參構造方法,將RunnableImpl實例作爲入參創建Thread實例;
- 調用其start方法
二、Thread的常用方法
1、join(加入)
在線程A中調用線程B的join方法後,直到B執行結束後,位於join方法後的A的代碼纔會被執行
/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
首先看不使用join方法的情況下,在main線程中啓動一個線程t,此時兩個線程併發執行,代碼如下:
public class TestJoin {
public static void main(String[] args) {
MyThread t = new MyThread("t");
t.start();
for (int i = 0; i < 100; i++) {
System.out.println("I am main thread");
}
}
}
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("I am " + getName());
}
}
}
部分輸出結果如下,可以看出main線程和t線程在交替執行
I am main thread
I am t
I am main thread
I am main thread
I am main thread
I am main thread
I am main thread
I am main thread
I am t
I am t
此時如果在啓動t線程後,接着調用t的join方法後,main線程只有等到t線程run方法執行結束後,纔會執行後面的代碼
public class TestJoin {
public static void main(String[] args) {
MyThread t = new MyThread("t");
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
System.out.println("I am main thread");
}
}
}
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("I am " + getName());
}
}
}
部分輸出結果如下,線程t先執行完,然後線程main纔會執行
I am t
I am t
I am t
I am t
I am t
I am t
I am main thread
I am main thread
I am main thread
I am main thread
I am main thread
I am main thread
I am main thread
注意:同時調用t1和t2的join方法後,t1和t2的執行情況是隨機的,互不干擾。且只有等到t1和t2都執行完,main方法後面的代碼纔會執行
public static void main(String[] args) {
MyThread t1 = new MyThread("t1");
MyThread t2 = new MyThread("t2");
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
System.out.println("I am main thread");
}
}
2、yield(讓步)
在當前線程中調用yield方法,表示告訴調度程序,當前線程願意讓出當前分到的時間片,這樣其他線程就可以獲取到更多的執行時間片
/**
* A hint to the scheduler that the current thread is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
*
* <p> Yield is a heuristic attempt to improve relative progression
* between threads that would otherwise over-utilise a CPU. Its use
* should be combined with detailed profiling and benchmarking to
* ensure that it actually has the desired effect.
*
* <p> It is rarely appropriate to use this method. It may be useful
* for debugging or testing purposes, where it may help to reproduce
* bugs due to race conditions. It may also be useful when designing
* concurrency control constructs such as the ones in the
* {@link java.util.concurrent.locks} package.
*/
public static native void yield();
首先正常啓動兩個線程t1、t2後,輸出結果一般是t1執行一段時間,t2執行一段時間
public class TestYield {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2("t1");
MyThread2 t2 = new MyThread2("t2");
t1.start();
t2.start();
}
}
class MyThread2 extends Thread {
public MyThread2(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("I am " + getName() + " , i = " + i);
}
}
}
部分輸出結果,可以看出結果是很隨機的
I am t1 , i = 0
I am t2 , i = 0
I am t1 , i = 1
I am t1 , i = 2
I am t1 , i = 3
I am t1 , i = 4
I am t1 , i = 5
I am t2 , i = 1
I am t1 , i = 6
I am t2 , i = 2
I am t1 , i = 7
如果在run方法中判斷當i % 10 == 0條件成立時就調用當前線程的yield,那麼當某個線程的i滿足該條件時,下一個獲取CPU時間片的一定是另外一個線程
public class TestYield {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2("t1");
MyThread2 t2 = new MyThread2("t2");
t1.start();
t2.start();
}
}
class MyThread2 extends Thread {
public MyThread2(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("I am " + getName() + " , i = " + i);
if (i % 10 == 0) {
Thread.yield();
}
}
}
}
3、setPriority(設置優先級)
優先級越高,線程獲取到的CPU執行時間片越多。在Thread類中有如下三個關於優先級的靜態變量,最小值爲1,最大值爲10,默認值爲5
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
如下代碼,將t2的優先級在默認值上加3,可以使t2獲取到更多的執行時間片
public class TestPriority {
public static void main(String[] args) {
MyThread3 t1 = new MyThread3("t1");
MyThread3 t2 = new MyThread3("t2");
t2.setPriority(Thread.NORM_PRIORITY + 3);
t1.start();
t2.start();
}
}
class MyThread3 extends Thread {
public MyThread3(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("I am " + getName() + " , i = " + i);
}
}
}
4、
三、線程的幾種狀態
1、new(新建)
未調用start方法的線程狀態
/**
* Thread state for a thread which has not yet started.
*/
NEW
static void state_new() {
Thread thread = new Thread();
System.out.println(thread.getState());/輸出結果爲NEW
}
2、RUNNABLE(運行)
正在被JVM執行的線程狀態
/**
* 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
/**
* 當JVM在執行run方法體時(注意:不要調用sleep方法),此時線程狀態爲RUNNABLE
* 當run方法體執行結束後,變成TERMINATED
*/
static void state_runnable() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
for (int j = 0; j < Integer.MAX_VALUE; j++) {
}
}
}
});
thread.start();
while (true) {
System.out.println(thread.getState());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
輸出結果如下
RUNNABLE
TERMINATED
TERMINATED
3、TERMINATED(終止)
run方法體執行結束的線程狀態,實驗代碼如上
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED
4、WAITING(等待)
當一個線程等待另外一個線程執行結束後才能繼續執行的線程狀態,可以通過調用無參的join()方法實現
/**
* 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
如下代碼,在main線程中調用thread的join方法後,main線程就一直處於WAITING狀態
static void state_waiting() {
//獲取main線程
Thread mainThread = Thread.currentThread();
//新建線程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("main線程狀態:" + mainThread.getState());;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
});
thread.start();
try {
//join方法表示直到thread線程執行結束後,main線程纔會繼續執行
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
5、TIMED_WAITING(定時等待)
當一個線程等待另外一個線程執行結束後才能繼續執行的線程狀態,可以通過調用有參的join(long)方法實現
/**
* 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
代碼如下,注意此時用的是join的有參方法
static void state_timed_waiting() {
//獲取main線程
Thread mainThread = Thread.currentThread();
//新建線程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("main線程狀態:" + mainThread.getState());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
});
thread.start();
try {
//join(5000)方法表示等待thread線程執行5000ms後,main線程就會繼續執行
thread.join(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main線程繼續執行");
}
輸出結果
main線程狀態:TIMED_WAITING
main線程狀態:TIMED_WAITING
main線程狀態:TIMED_WAITING
main線程狀態:TIMED_WAITING
main線程狀態:TIMED_WAITING
main線程繼續執行
main線程狀態:TERMINATED
main線程狀態:TERMINATED
main線程狀態:TERMINATED