10.1線程的概念:java
事實上,在單個程序內部是能夠在同一時刻進行多種運算的,這就是所謂的多進程(這與多任務的概念有類似之處)。、程序員
一個單獨的進程與順序程序類似,也有一個入口,一個出口,以及一個順序執行的序列。從概念上說,一個線程是一個程序內部的一個順序控制流。線程並非程序,它本身自己並不能運行,而必須在程序中運行。在一個程序中能夠實現多個進程,這些進程同時運行,可是多線程並不等於屢次啓動一個程序,操做系統也不會把每一個線程當作進程來對待。web
線程如進程的區別:編程
1:二者的顆粒度不一樣,是兩個不一樣層次上的概念。進程是由操做系統來管理的,而線程則是在一個程序(進程)內。安全
2:不一樣的的代碼,內部數據和狀態都是徹底獨立的,而一個程序內的多線程共享同一塊內存空間和同一組系統資源,有可能互相影響。網絡
3:線程自己的數據一般只有寄存器數據,以及一個程序執行時使用的堆棧,因此線程的切換負擔比進程切換有的要小。多線程
使用多線程具備的以下的優勢:併發
1:多線程編程簡單,效率高(能直接共享數據和資源,而多進程不能)ide
2:適合於開發服務程序(如web服務,聊天服務)工具
3:適合於開發有多種交互接口的程序(如聊天程序的客戶端,網路下載工具)
4:適合於有人機交互又有計算量的程序
5:減輕編寫交互頻繁,涉及面多的程序的困難(如監聽網絡接口)
6:程序的吞吐量會獲得改善
7:有多個處理器的系統,能夠併發運行不一樣的線程
10.2線程的創建
Java提供了類Java.lang.Thread來支持多線程編程,Thread類有如下的構造方法
Thread() |
Thread(Runable target) |
Thread(Runable target,String name) |
Thread(String name) |
Thread(ThreadGroup group,Rnuable target) |
Thread(ThreadGroup group,Rnuable target,String name) |
Thread(ThreadGroup group,String name) |
參數target是線程執行的目標對象,即線程執行的代碼;group是線程所在的組;name是線程的名字
10.2.1採用繼承法建立線程
該方法比較簡單,主要是經過繼承java.lang.Thread類,並覆蓋Thread類的run()方法來完成線程的建立.Thread類是一個具體的類不是抽象類,該類封裝了線程的行爲.要建立一個線程,程序員必須建立一個Thread的子類.在Thread類中最重要的方法時run()和start().
Run()方法必須進行重寫,把線程所要執行的代碼加入到這個方法中,也就是線程體.可是它必須通過start()方法來啓動線程.
public class MyThread extends Thread
{
// count變量用於統計打印的次數並共享變量
private static int count = 0;
public static void main(String[] args)
{
MyThread p = new MyThread("t1");
// 線程執行
p.start();
// 主線程main方法執行一個循環
for (int i = 0; i < 5; i++)
{
count++;
System.out.println(count + " :main");
}
}
// 構造方法
public MyThread(String name)
{
// 調用父類的構造方法
super(name);
}
@Override
public void run()
{
// 線程中必須有的run方法
for (int i = 0; i < 5; i++)
{
count++;
System.out.println(count + ":" + this.getName());
}
}
}
10.2.2 經過實現接口建立線程
該方法經過實現java.lang.Runnable接口的類來建立多線程.該接口定義了一個方法run(),因此必須的新類中實現它,可是Runnable接口中沒有任何對線程的支持,還必需要建立Thread類的實例
public class MyThread2 implements Runnable
{
public static void main(String[] args)
{
for (int i = 0; i < 5; i++)
{
new Thread(new MyThread2(i + 1)).start();
}
}
int count, number;
public MyThread2(int i)
{
this.number = i;
System.out.println("建立線程 " + this.number);
}
public void run()
{
while (true)
{
System.out.println("線程 " + this.number + ":計數 " + this.count);
if (++this.count == 6)
{
return;
}
}
}
}
10.3線程的生命週期及調度
10.3.1 線程的生命週期
線程是動態的,具備必定的生命週期,分別從建立,執行,堵塞直到死亡.在每個線程類中都定義了用於完成功能的run方法,這個方法稱爲線程體
1:線程的四個狀態
1.1:建立狀態:
當利用new關鍵字建立線程對象實例後,它僅僅做爲一個對象實例存在,JVM沒有爲其分配CPU時間片等線程資源
1.2:就緒狀態:
當處於建立狀態的線程中調用start方法將線程的狀態轉換爲就緒狀態.
1.3:堵塞狀態
堵塞指的是暫停一個線程的執行以等待某個條件發生(如某資源準備就緒),若線程處於堵塞狀態,調度機制不給它分配任何CPU時間,直接跳過它.
1.4:死亡狀態
當線程運行結束或者在調用線程對象的stop方法後線程將終止運行,由JVM收回線程佔用的資源
10.3.2:線程調度和優先級
Java採用的是一種簡單,固定的調度法,即固定優先級調度(搶先式調度),Java將線程的優先級分爲10個等級,分別用1到10的數字表示.數字越大代表線程的級別越高.相應的,在Thread類中定義了表示線程最高,最低,和普通優先級的常量MIN_PRIORITY,MAX_PRIORITY,NORMAL_PRIORITY,表明的優先級分別是1,10,5,當一個線程對象被建立時,其默認的優先級是5.
在應用程序中設置線程優先級的方法比較簡單,在建立線程對象後能夠調用線程對象的setPriority()方法該表線程的優先級,一樣能夠調用getPriority()來獲取當前線程的優先級.
public class TestThreadPriority extends Thread
{
public static void main(String[] args)
{
TestThreadPriority t1 = new TestThreadPriority("Thread1");
t1.setPriority(MIN_PRIORITY);
t1.start();
TestThreadPriority t2 = new TestThreadPriority("Thread2");
t2.setPriority(NORM_PRIORITY);
t2.start();
TestThreadPriority t3 = new TestThreadPriority("Thread3");
t3.setPriority(MAX_PRIORITY);
t3.start();
}
public TestThreadPriority(String name)
{
super(name);
}
@Override
public void run()
{
for (int i = 0; i < 3; i++)
{
System.out.println(this.getName() + " is running");
}
super.run();
}
}
10.4:線程互斥
在併發程序設計中已經被研究並獲得解決.對多線程共享的資源或數據稱爲臨界資源,而把每個線程中訪問臨界資源的那一段代碼稱爲臨界代碼.經過爲臨界帶買段設置信號燈,就能夠保證資源的完整性,從而安全地訪問共享資源.
爲了實現這種機制,Java語言提供瞭如下兩方面的支持
爲每一個對象設置了一個」互斥鎖」標記.該標記保證在任什麼時候候,只能有一個線程有該互斥鎖,其餘線程若是須要得到互斥鎖,必須等待當前擁有該鎖的線程將其釋放.該對象稱爲互斥對象.
爲了配合使用對象的互斥鎖,Java語言提供了保留字synchronized.其基本用法以下:
Synchronized(互斥對象){
臨界代碼
}
當一個線程執行到該行代碼時,首先檢測該互斥對象的互斥鎖.若是該互斥鎖沒有被佔用,則該線程將得到該互斥鎖,並執行臨界代碼,直到執行完畢並釋放互斥鎖;
能夠看出,任意一個對象均可以做爲信息燈,從而解決上面的問題,首先定義一個互斥對象類,做爲信號燈.因爲該對象只做爲信號量使用,因此並不須要爲它定義其餘方法.
public class AccountThread extends Thread
{
public static void main(String[] args)
{
Account account = new Account(100);
Semaphore semaphore = new Semaphore();
AccountThread at1 = new AccountThread(account, 1000, semaphore);
AccountThread at2 = new AccountThread(account, 0, semaphore);
at1.start();
at2.start();
}
Account account;
int delay;
Semaphore semaphore;
// 構造方法
public AccountThread(Account account, int delay, Semaphore semaphore)
{
this.account = account;
this.delay = delay;
this.semaphore = semaphore;
}
@Override
public void run()
{
synchronized (this.semaphore)
{
if (this.account.balance >= 100)
{
try
{
// 延遲
sleep(this.delay);
// 模擬取錢100
this.account.balance = this.account.balance - 100;
System.out.println("withdraw 100 successful!!!");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
else
{
System.out.println("withdraw failed!!!");
}}}}
// 定義一個類,利用其對象做爲互斥信號燈
class Semaphore{}