線程的概念
單純種以一個任務完成以后再進行下一個任務的模式進行,這樣下一個任務的開始必須等待前一個任務的結束,只有一個任務完成后才能進行下一個任務。Java 語言提供了並發機制,允許開發人員在程序中執行多個線程,每個線程完成一個功能,並與其他線程並發執行。這種機制被稱為多線程。操作系統以進程為單位,我們下Windows
系統可以分配給每個進程一段有限的執行 CPU 的時間(也稱為 CPU 時間片),CPU 在這段時間中執行某個進程,然后下一個時間段又跳到另一個進程中去執行。由於 CPU 切換的速度非常快,給使用者的感受就是這些任務似乎在同時運行,所以使用多線程技術后,可以在同一時間內運行更多不同種類的任務。
單任務的特點就是排隊執行,也就是同步,就像在 cmd 中輸入一條命令后,必須等待這條命令執行完才可以執行下一條命令一樣。
CPU 完全可以在任務 1 和任務 2 之間來回切換,使任務 2 不必等到 5 秒再運行,系統的運行效率大大得到提升。這就是要使用多線程技術,線程可以理解成是在進程中獨立運行的子任務。
實現方式
實現多線程編程的方式主要有兩種:一種是繼承 Thread 類,另一種是實現 Runnable 接口
Thread類
public Thread(String threadName)
public Thread()
public class NewThread extends Thread{
@Override
public void run(){
//線程的執行代碼
}
}
//使用
new NewThread().start();
線程實現的業務代碼需要放到 run() 方法中。當一個類繼承 Thread 類后,就可以在該類中覆蓋 run() 方法,將實現線程功能的代碼寫入 run() 方法中,然后同時調用 Thread 類的 start() 方法執行線程,也就是調用 run() 方法
Runnable接口
如果要創建的線程類已經有一個父類,這時就不能再繼承 Thread 類,因為 Java 不支持多繼承,所以需要實現 Runnable 接口來應對這樣的情況
public Thread(Runnable r);
public Thread(Runnable r,String name);
public class MyRunnable implements Runnable{
@Override
public void run(){
//線程的執行代碼
}
}
//使用
Runnable runnable=new MyRunnable();
Thread thread=new Thread(runnable);
thread.start();
線程的生命周期
線程也具有生命周期,主要包括 7 種狀態,分別是出生狀態、就緒狀態、運行狀態、等待狀態、休眠狀態、阻塞狀態和死亡狀態
出生狀態:用戶在創建線程時所處的狀態,在用戶使用該線程實例調用 start() 方法之前,線程都處於出生狀態。
就緒狀態:也稱可執行狀態,當用戶調用 start() 方法之后,線程處於就緒狀態。
運行狀態:當線程得到系統資源后進入運行狀態。
等待狀態:當處於運行狀態下的線程調用 Thread 類的 wait() 方法時,該線程就會進入等待狀態。進入等待狀態的線程必
須調用 Thread 類的 notify() 方法才能被喚醒。notifyAll() 方法是將所有處於等待狀態下的線程喚醒。
休眠狀態:當線程調用 Thread 類中的 sleep() 方法時,則會進入休眠狀態。
阻塞狀態:如果一個線程在運行狀態下發出輸入/輸出請求,該線程將進入阻塞狀態,在其等待輸入/輸出結束時,線程進入就緒狀態。對阻塞的線程來說,即使系統資源關閉,線程依然不能回到運行狀態。
死亡狀態:當線程的 run() 方法執行完畢,線程進入死亡狀態。
同步機制 synchronized
為了處理這種共享資源競爭,可以使用同步機制。所謂同步機制,指的是兩個線程同時作用在一個對象上,應該保持對象數據的統一性和整體性。Java 提供 synchronized 關鍵字,為防止資源沖突提供了內置支持。
同步方法
class className{
public synchronized type methodName(){
//代碼
}
}
同步塊
synchronized(obj){
//代碼
}
public class test{
Object obj=new Object();
public void method(){
synchronized(obj){
//代碼
}
}
}
同步的多線程與單線程有本質的區別,當處理一段非常復雜的業務時,使用了多線程處理,只有被synchronized的代碼塊才會被進行順序執行,其他的業務代碼都是在不同的線程里各自執行。
curentThread()
返回代碼段正在被哪個線程調用的線程相關信息
public static void main(String[] args)
{
//調用currentThread()方法輸出當前線程名稱
System.out.println(Thread.currentThread().getName());
}
public class MyThread extends Thread{
@Override
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
MyThread myThread=new MyThread();
Thread t = new Thread(myThread);
t.setName("myThread");
t.start();//輸出myThread;
isAlive()
isAlive() 方法的作用是判斷當前的線程是否處於活動狀態。什么是活動狀態呢?活動狀態就是線程已經啟動且尚未終止。線程處於正在運行或准備開始運行的狀態,就認為線程是“存活”的
public class MyThread extends Thread
{
@Override
public void run()
{
System.out.println("run="+this.isAlive());
}
}
public static void main(String[] args)
{
MyThread mythread=new MyThread();
System.out.println("begin="+mythread.isAlive()); //輸出線程狀態
mythread.start(); //啟動線程
System.out.println("end="+mythread.isAlive()); //輸出線程狀態
}
//輸出
begin==false
end==true或false//這里要注意,由於另啟一個線程,去執行代碼,end有可能在線程啟動前執行(此情況end為false),也有可能在線程啟動后執行(此情況end為ture)
run=true
sleep()
sleep() 方法的作用是在指定的毫秒數內讓當前“正在執行的線程”休眠(暫停執行)
public class MyThread extends Thread
{
@Override
public void run()
{
System.out.println("開始");
Thread.sleep(2000); //延時2秒
System.out.println("結束");
}
}
getId()
getId() 取得正在運行線程的唯一標識
Thread thread=Thread.currentThread();
System.out.println(thread.getId());
線程暫停
暫停線程意味着此線程還可以恢復運行。使用 suspend() 方法暫停線程,使用 resume() 方法恢復線程的執行
public class MyThread extends Thread
{
private long i=0;
public long getI()
{
return i;
}
public void setI(long i)
{
this.i=i;
}
@Override
public void run()
{
while(true)
{
i++;
}
}
}
public static void main(String[] args){
MyThread thread=new MyThread();
thread.start();
System.out.println("線程開始");
System.out.println(System.currentTimeMillis()+" i= "+thread.getI());//輸出i的值
thread.suspend();//暫停
System.out.println("線程暫停5秒");
Thread.sleep(5000);
thread.resume();//開始
System.out.println(System.currentTimeMillis()+" i= "+thread.getI());//繼續暫停后i的值
}
注意:在使用 suspend() 方法與 resume() 方法時,如果使用不當極易造成公共的同步對象被獨占,從而使得其他線程無法訪問公共同步對象。使用時要格外注意。
線程停止
停止一個線程意味着在線程處理完任務之前停掉正在做的操作,也就是放棄當前的操作。有三種方法可以停止線程
使用退出標識,使線程正常退出,也就是當 run() 方法完成后線程終止。
使用 stop() 方法強行終止線程,但是不推薦使用這個方法,因為 stop() 和 suspend() 及 resume() 一樣,都是作廢過期的方法,使用它們可能產生不可預料的結果。
使用 interrupt() 方法中斷線程。
interrupt()
interrupt() 方法的作用是用來停止線程,但 intermpt() 方法的使用效果並不像循環結構中 break 語句那樣,可以馬上停止循環。調用 intermpt() 方法僅僅是在當前線程中打了一個停止的標記,並不是真的停止線程
public class MyThread extends Thread
{
@Override
public void run()
{
for (int i=0;i<10000;i++)
{
System.out.println(i+1);
}
}
}
public static void main(String[] args){
MyThread thread=new MyThread(); //創建MyThread13線程類實例
thread.start(); //啟動線程
Thread.sleep(100); //延時100毫秒
thread.interrupt(); //停止線程
}
主線程的運行結果如下所示。從中可以看到,雖然在延時 100 毫秒后調用 intermpt() 方法停止了 thread 線程,但是該線程仍然執行完成輸出 10000 行信息。
判斷線程是不是停止狀態
this.interrupted():測試當前線程是否已經中斷。
this.islnterrupted():測試線程是否已經中斷。
stop()
調用 stop() 方法可以在任意情況下強制停止一個線程
public class MyThread extends Thread{
private int i=0;
@Override
public void run(){
while (true){
i++;
System.out.println("i=" + i);
Thread.sleep(1000);
}
}
}
MyThread thread=new MyThread();
thread.start();
Thread.sleep(8000);
thread.stop();
線程在啟動后有一個 8000 毫秒的延時,在這段時間內會循環 9 次,之后 stop() 方法被執行從而線程停止。運行后輸出1到9
調用 stop() 方法時會拋出 java.lang.ThreadDeath 異常,但在通常情況下,此異常不需要顯式地捕捉。使用 stop() 釋放鎖將會給數據造成不一致性的結果。如果出現這樣的情況,程序處理的數據就有可能遭到破壞,最終導致程序執行的流程錯誤,一定要特別注意。