java線程(一)
線程的生命週期及五種基本狀態
新建狀態(New):當線程對象對創建後,即進入了新建狀態,如:Thread t = new MyThread();
就緒狀態(Runnable):當調用線程對象的start()方法(t.start();),線程即進入就緒狀態。處於就緒狀態的線程,只是說明此線程已經做好了準備,隨時等待CPU調度執行,並不是說執行了t.start()此線程立即就會執行;
運行狀態(Running):當CPU開始調度處於就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。注:就 緒狀態是進入到運行狀態的唯一入口,也就是說,線程要想進入運行狀態執行,首先必須處於就緒狀態中;
阻塞狀態(Blocked):處於運行狀態中的線程由於某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被CPU調用以進入到運行狀態。根據阻塞產生的原因不同,阻塞狀態又可以分爲三種:
等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態;
同步阻塞 – 線程在獲取synchronized同步鎖失敗(因爲鎖被其它線程所佔用),它會進入同步阻塞狀態;
其他阻塞 – 通過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
實現線程的基本方法
實現runnable接口
package cn.sc.thread;
public class BaseThread {
public static void main(String[] args) {
Runnable myRunnable = new MyRunnable();
myRunnable.run();
new Thread(myRunnable).start();
}
}
class MyRunnable implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName()+" my runnable");
}
}
運行之後輸出
main my runnable
Thread-0 my runnable
繼承Thread
public class BaseThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myRunnable.run();
myThread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+ " my thread");
}
}
運行後輸出
main my thread
Thread-0 my thread
解析
可以看到,如果直接調用run方法,並沒有啓動新的線程,而是繼續使用主線程運行run方法。直接執行run方法就和實現一個普通類一樣。
查看Thread類的源碼可以得知
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) {
}
}
}
private native void start0();
通過navtive的方法調用jre的本地方法庫,操作cpu開啓了新的線程。
通過new Thread()
之後線程進入new
狀態
通過thread.start()
之後將新建的線程從new
狀態進入runnable
狀態
實現Callable
public class ThreadTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask<String> stringFutureTask = new FutureTask<>(myCallable);
Thread thread = new Thread(stringFutureTask);
thread.start();
String s = stringFutureTask.get();
System.out.println(s);
}
}
class MyCallable implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName()+ " my callable");
return "myCallable";
}
}
輸出
Thread-0 my callable
myCallable
幾個例子
通過runnable創建thread,會執行誰
package cn.sc.thread;
public class ThreadTest {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
MyRunnable myRunnable = new MyRunnable();
MyThread myThread = new MyThread(myRunnable);
myThread.start();
}
}
class MyRunnable implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName()+" my runnable");
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+ " my thread");
}
}
運行結果爲
main
Thread-0 my thread
啓動線程後,線程的執行體爲run方法。
爲什麼調用了MyThread的run方法而沒有調用MyRunnable的run方法呢?
Thread類實現了Runnable接口
@Override
public void run() {
if (target != null) {
target.run();
}
}
這個target就是新建線程之後傳進去的MyRunnable對象,但是爲什麼沒有執行呢?
因爲MyThread類重寫了Thread的run方法,因此在執行時由於java的多態特性,就會執行子類的重寫後的run方法了。
同一個thread可以啓動兩次嗎
package cn.sc.thread;
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
myThread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+ " my thread"+i);
}
}
}
運行後輸出
Thread-0 my thread0
Thread-0 my thread1
Thread-0 my thread2
Thread-0 my thread3
Thread-0 my thread4
Thread-0 my thread5
Thread-0 my thread6
Thread-0 my thread7
Thread-0 my thread8
Thread-0 my thread9
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at cn.sc.thread.ThreadTest.main(ThreadTest.java:10)
同一個線程只能start一次。
如上面所講的,
if (threadStatus != 0)
throw new IllegalThreadStateException();
start方法將新建的線程從new
狀態進入runnable
狀態,
第一次start之後,這個線程就進入了runnable狀態了,並修改threadStatus
第二次再start的時候,threadStatus已經不是0了,就會拋出這個錯誤了