並發編程(01)--多線程基礎
1.關於多線程
1.1 線程與進程的區別
進程:系統中每一個正在運行的程序都是一個進程,每一個進程執行都有一個執行順序,該順序是一個執行路徑,或者叫一個控制單元
線程:是一組指令的集合,控制著進程的執行,一個進程中至少有一個線程
1.2 為什麽用多線程
使用多線程可以將執行時間長的程序中的任務放到後臺去處理,在一些需要等待的任務上如文件讀寫、網絡分發數據等,使用多線程就可以更好的利用CPU的資源,從而提高程序運行的效率
2. 線程的狀態
線程有5種狀態:新建、就緒、運行、阻塞、死亡,如下圖所示:
新建狀態:當new一個線程時, 例如new Thread(r),線程還沒有開始運行,此時線程處在新建狀態。 當一個線程處於新生狀態時,程序還沒有開始運行線程中的代碼
就緒狀態:一個新創建的線程並不自動開始運行,要執行線程,必須調用線程的start()方法。當線程對象調用start()方法即啟動了線程,start()方法創建線程運行的系統資源,並調度線程運行run()方法。當start()方法返回後,線程就處於就緒狀態。處於就緒狀態的線程並不一定立即運行run()方法,線程還必須同其他線程競爭CPU時間,只有獲得CPU時間才可以運行線程。因為在單CPU的計算機系統中,不可能同時運行多個線程,一個時刻僅有一個線程處於運行狀態。因此此時可能有多個線程處於就緒狀態。對多個處於就緒狀態的線程是由java運行時系統的線程調度程序(thread scheduler)來調度的。
運行狀態:當線程競爭到CPU時間之後,執行run()方法,進入運行狀態
阻塞狀態:線程運行過程中,可能由於各種原因進入阻塞狀態:
1)線程通過調用sleep方法進入睡眠狀態;
2)線程調用一個在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會返回到它的調用者;
3)線程試圖得到一個鎖,而該鎖正被其他線程持有;
4)線程在等待某個觸發條件;
所謂阻塞狀態是正在運行的線程沒有運行結束,暫時讓出CPU,這時其他處於就緒狀態的線程就可以獲得CPU時間,進入運行狀態。
死亡狀態:有兩種原因會導致線程進入死亡狀態:
1)run()方法執行完成正常死亡
2)發生異常終止了run()方法而進入死亡狀態
3. 創建線程的方法
3.1 繼承Thread類,重寫run()方法
class CreateThread extends Thread { publicvoid run() { // run方法中編寫 多線程需要執行的代碼 } }
3.2 實現Runnable接口,重寫run()方法
class CreateRunnable implements Runnable { @Override publicvoid run() { //run方法中編寫多線程需要執行的代碼 } }
3.3 匿名內部類方式
Thread thread = new Thread(new Runnable() { public void run() { //run方法中編寫多線程需要執行的代碼 } });
4. 常用的方法
4.1 join()
join():當線程B執行到了線程A的.join()方法時,B線程就會等待,等A線程都執行完畢,B線程才會執行,join可以用來臨時加入線程執行。
public class Thread002 { public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.print("這個是t1線程:" + i +"\t"); } System.out.println("\n--------------分割線------------------"); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { System.out.print("這個是t2線程:" + i+ "\t"); } System.out.println("\n--------------分割線------------------"); } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { try { t2.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for (int i = 0; i < 10; i++) { System.out.print("這個是t3線程:" + i +"\t"); } } }); t1.start(); t2.start(); t3.start(); } }
4.2 sleep()、yield()
sleep():讓正在執行的線程休眠,因為使用sleep方法之後,線程是進入阻塞狀態的,只有當睡眠的時間結束,才會重新進入到就緒狀態,而就緒狀態進入到運行狀態,是由系統控制的,我們不可能精準的去幹涉它,所以如果調用Thread.sleep(1000)使得線程睡眠1秒,可能結果會大於1秒
public class SynTest { public static void main(String[] args) { new Thread(new CountDown(),"倒計時").start(); } } class CountDown implements Runnable{ int time = 10; public void run() { while (true) { if(time>=0){ System.out.println(Thread.currentThread().getName() + ":" + time--); try { Thread.sleep(1000); //睡眠時間為1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } } }
yield():和sleep方法類似,也是Thread類提供的一個靜態方法,可以讓正在執行的線程暫停,但是不會進入阻塞狀態,而是直接進入就緒狀態。相當於只是將當前線程暫停一下,然後重新進入就緒的線程池中,讓線程調度器重新調度一次。也會出現某個線程調用yield方法後暫停,但之後調度器又將其調度出來重新進入到運行狀態
public class SynTest { public static void main(String[] args) { yieldDemo ms = new yieldDemo(); Thread t1 = new Thread(ms,"張三吃完還剩"); Thread t2 = new Thread(ms,"李四吃完還剩"); Thread t3 = new Thread(ms,"王五吃完還剩"); t1.start(); t2.start(); t3.start(); } } class yieldDemo implements Runnable{ int count = 20; public void run() { while (true) { if(count>0){ System.out.println(Thread.currentThread().getName() + count-- + "個瓜"); if(count % 2 == 0){ Thread.yield(); //線程讓步 } } } } }
並發編程(01)--多線程基礎