### 一、簡介
> 多線程是程序中有多個線程流在同時調度資源。線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),一個線程可以創建和撤銷另一個線程。
### 二、線程和進程
1.進程是程序運行的實例,每一個進程都有自己的內存空間,包含內容和數據,不同進程間有相互獨立的地址空間
2.線程是CPU調度的基本單位,每一個線程都有順序執行的,線程有共享資源和鎖機制。
3.兩者區別和聯系
(1) 劃分尺度:線程更小,所以多線程程序并發性更高;
(2) 資源分配:進程是資源分配的基本單位,同一進程內多個線程共享其資源;
(3) 地址空間:進程擁有獨立的地址空間,同一進程內多個線程共享其資源;
(4) 處理器調度:線程是處理器調度的基本單位;
(5) 執行:每個線程都有一個程序運行的入口,順序執行序列和程序的出口,但線程不能單獨執行,必須組成進程,
一個進程至少有一個主線程。簡而言之,一個程序至少有一個進程,一個進程至少有一個線程
#### 三、線程的創建和啟動
線程創建有2中方式,一是實現Runnable接口,實現run()方法,然后創建一個Thread對象,將
而是繼承Thread類,實現run方法。兩者都需要調用start()方法來啟動線程。下面是兩者方法的程序實現:
1.繼承Thread方式
~~~
public class Demo1 extends Thread {
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
~~~
2.實現Runnable接口
~~~
public class Demo2 implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
~~~
### 四、線程的狀態
線程運行時也有它的生命周期,線程會要經歷開始(等待),運行,掛起(阻塞)和終止四種不同的狀態,且四種狀態可以由Thread來自由控制,下面給出Thread類控制各個狀態的方法
> 1.線程開始 start()/run();
2.線程掛起和喚醒 resume()/suspend()-已過時 sleep();
3.線程終止 stop()不建議使用 interupt();
4.其他與線程狀態相關的方法
isAlive():判斷線程的狀態是否還活著
join():調用某線程的該方法,將當前線程與該線程“合并”,即等待該線程結束,再恢復當前線程的運行;
yield():線程的讓步,即讓出當前線程的資源給其他線程使用。
狀態圖如下:

從圖中可看出線程在建立后并不馬上執行run方法中的代碼,而是處于等待狀態。線程處于等待狀態時,可以通過Thread類的方法來設置線程不各種屬性,如線程的優先級(setPriority)、線程名(setName)和線程的類型(setDaemon)等。
- 當調用start方法后,線程開始執行run方法中的代碼。線程進入運行狀態。可以通過Thread類的isAlive方法來判斷線程是否處于運行狀態。
- 當線程處于運行狀態時,isAlive返回true,
- 當isAlive返回false時,可能線程處于等待狀態,也可能處于停止狀態。
***注意:***一但線程開始執行run方法,就會一直到這個run方法執行完成這個線程才退出。但在線程執行的過程中,可以通過兩個方法使線程暫時停止執行。這兩個方法是suspend和sleep。
在使用suspend掛起線程后,可以通過resume方法喚醒線程。而使用sleep使線程休眠后,只能在設定的時間后使線程處于就緒狀態(在線程休眠結束后,線程不一定會馬上執行,只是進入了就緒狀態,等待著系統進行調度)。
**在使用sleep方法時有兩點需要注意:**
1. sleep方法有兩個重載形式,其中一個重載形式不僅可以設毫秒,而且還可以設納秒(1,000,000納秒等于1毫秒)。但大多數操作系統平臺上的Java虛擬機都無法精確到納秒,因此,如果對sleep設置了納秒,Java虛擬機將取最接近這個值的毫秒。
1. 在使用sleep方法時必須使用throws或try{…}catch{…}。因為run方法無法使用throws,所以只能使用try{…}catch{…}。當在線程休眠的過程中,使用interrupt方法中斷線程時sleep會拋出一個InterruptedException異常。sleep方法的定義如下:
publicstaticvoid sleep(long millis) throws InterruptedException
publicstaticvoid sleep(long millis, int nanos) throws
InterruptedException
下面的舉例為線程的合并的實現:
#### A線程:
~~~
public class DemoA extends Thread {
public DemoA(String name){
super(name);
}
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"-"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
~~~
#### B線程:
~~~
public class DemoB extends Thread {
private Thread a;
public DemoB(String name,Thread a){
super(name);
this.a=a;
}
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"-"+i);
try {
Thread.sleep(1000);
if(i==4){
a.join();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
~~~
### 五、線程的屬性
線程的屬性包括線程的優先級,守護線程等,明白線程的屬性的作用,可以更靈活的設置線程的執行狀態。
#### 1.線程的優先級
每一個線程都會對應一個優先級,默認情況下,新創建的線程會繼承他父類的優先級。可以利用setPriority方法來修改線程的優先級的高低。修改的范圍可以使MIN_PRIORITY和MAX_PRIORITY之間的任意級別
下面為線程優先級設置的實例:
***定義的線程類:***
~~~
public class Demo3 implements Runnable {
@Override
public void run() {
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+"-"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
~~~
***線程優先級的設置:***
~~~
//最小級別
Thread t1=new Thread(new Demo3());
t1.setPriority(Thread.MIN_PRIORITY);
t1.start();
//正常
Thread t2 =new Thread(new Demo3());
t2.setPriority(Thread.NORM_PRIORITY);
t2.start();
//最大級別
Thread t3 =new Thread(new Demo3());
t3.setPriority(Thread.NORM_PRIORITY);
t3.start();
}
~~~
注意:在使用線程優先級時,應避免常犯的一個錯誤,假如高優先級的線程處于非活躍狀態,低優先級的線程也不可能會執行,而資源調度線程時會在高優先級的線程中選擇,這會是低優先級的線程餓死。
##### 2.守護線程
一般線程要轉換為守護線程,可通過setDaemon(true);來設置,守護線程不會去訪問固有資源,如文件,數據庫等。其作用是為其他線程提供服務,如計時器的例子,守護線程可定時發送
信號給實現計時的線程。
#### 3.未捕獲異常的處理器
線程實現run方法時不會拋出可被檢測的異常,而拋出的異常不能被檢測到會導致線程終止,從而是程序死亡。
java提供了一個未捕獲異常的處理器,該處理器為Thread.UncaughTExceptionHandler接口的類。從JSE5.0后,提供了setUncaughTExceptionHandler方法為線程安裝處理器.