線程和進程的基本概念
進程和線程是動態的概念。
進程是 “執行中的程序“,是一個動詞,而程序是一個名詞,進程運行中程序的”代碼“,而且還有自己的計數器,寄存器,它會向系統申請系統資源。
線程是進程中的一個控制流。一個程序可能可能包含多個任務並發運行,而線程就是指一個任務重頭到尾的執行流。
說的在簡單點,線程是執行中的任務,一個程序包含多個任務。
多線程
單處理器中,為提高處理器的使用率(最終目標),使得程序在進行IO輸入出等不需要處理器時,也能夠讓處理器在運轉,引進多線程處理機制。多線程可以使得程序運行的更快,執行效率更高,交互性更強,這是不言而喻的!
創建任務和線程
一個任務是一個對象,所以為創建一個任務,必須定義一個類,定義一個任務類,為了說明這是一個任務類,它需要實現Runnable接口,這個接口只包含一個run方法。
當我們定義好任務類taskClass之后,就可以用它的構造方法創建一個任務啦:TaskClass task = new TaskClass(.....);
我們創建的任務只能在線程中運行,Thread類中包含了創建線程以及控制線程的眾多方法。使用下面的語句創建任務線程:Thread thread = new Thread(task);
然后調用start()方法告訴java虛擬機該線程准備運行:thread.start();之后java虛擬機通過調用任務的run()方法執行任務。
事例代碼:
public class TaskThreadDemo{ public static void main(String[] args) { //創建任務 並且需要為任務 創建 任務類,使用該類的構造方法創建任務 PrintChar printA = new PrintChar('a',100); PrintChar printB = new PrintChar('b',100); PrintNum print100 = new PrintNum(100); //為任務創建線程 Thread thread1 = new Thread(printA); Thread thread2 = new Thread(printB); Thread thread3= new Thread(print100); //告訴虛擬機器線程開始運行 thread1.start(); thread2.start(); thread3.start(); } } class PrintChar implements Runnable { private char charToPrint; private int items; public PrintChar(char c,int t) { charToPrint = c; items = t; } @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<items;i++) { System.out.print(charToPrint); } } } class PrintNum implements Runnable { private int lastNumb; public PrintNum(int n) { lastNumb = n; } @Override public void run() { // TODO Auto-generated method stub for(int i= 1;i<100;i++) { System.out.print(" "+lastNumb); } }
}
一個好玩的閃爍文本框,直接用線程,不用main方法
import javax.swing.JApplet; import javax.swing.JLabel; public class FlashingText extends JApplet implements Runnable { private static final long serialVersionUID = 1L; private JLabel jlbText = new JLabel("Welcome",JLabel.CENTER); public FlashingText() { add(jlbText); new Thread(this).start(); } public void run() { try { while(true) { if(jlbText.getText()==null) jlbText.setText("Welcome"); else jlbText.setText(null); Thread.sleep(200); } } catch (Exception e) { // TODO: handle exception } } }
Thread類
Thread類實現了Runnable接口,它包含為任務而創建線程的構造方法,以及控制方法,下圖就是Thread類常見的控制線程的方法:
因為Thread類實現了Runnable接口,所以可以定義一個Thread的擴展類,里面實現run方法,這樣也可以創建一個線程類,但並不是很推薦這種方法,因為它將創建任務和運行任務的機制混在了一起,將任務從線程中分離出來比較好,即盡量使用Runnable接口創建線程,這樣得到的線程更加靈活,避免了java單繼承帶來的局限性。
線程池
線程池是管理並發執行任個數的理想方法,java提供Executor接口來執行線程池中的任務,提供ExecutorService接口來管理和控制任務。為了創建Executor接口實例,我們可以用Executors類,Executors類提供了創建Executor接口對象的靜態方法,下圖描述了上面的上面所說的關系。
線程池中的shutdown()方法一般都是放在main方法的后面部分,當所以的線程都添加到線程池中,即便有線程沒有執行完畢,也可能會關閉線程池,未執行完的線程繼續執行,所以main方法可能比子線程先結束。
利用isTerminated()進行線程池中所有線程運行時間的統計
倘若希望主線程在子線程全部做完之后在執行,可以考慮讓所以的子線程用jion方法,但這可能導致所有子線程變成串行,不是很好的辦法,當然我們也可以多線程的輔助類CountDownLatch,這是一個與計時器有點類似功能的類。這次在看書學習的時候,發現了一個更好的辦法,就是在shutdown()方法后加上一句while(!executor.isTerminated()){},這是一個不錯的方法(不過我把shoudown()方法放在該while語句后面,就會陷入死循環,應該是要先關閉線程池,在判斷線程池中的線程是否全部終止,因為也有可能在while語句后面在添加新的子線程)。
isTerminated()方法:如果線程池中所以線程都已完成並終止,則返回true.
可以使用CountDownLatch 和上面的方法對程序運行計時統計,不過我記得CountDownLatch類是需要指定線程的個數。
事例代碼
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorDemo { public static void main(String[] args){ long start = System.currentTimeMillis(); System.out.println("當前時間"+" "+start); ExecutorService executor = Executors.newFixedThreadPool(4); executor.execute(new PrintChar('a',100)); executor.execute(new PrintChar('b',100)); executor.execute(new PrintChar('c',100)); executor.execute(new PrintNum(100)); executor.shutdown(); while(!executor.isTerminated()){ //System.out.print("-"); }; long end = System.currentTimeMillis(); System.out.println("\n當前時間"+" "+end); System.out.println("\n用時"+" "+(end-start)+"毫秒"); } }
運行截圖
截圖1
截圖2
不過不知道運行太快的原因,還是啥別的原因, 會出現截圖2的情況,網上很少關於isTerminated()方法的介紹,如有大神知道原因,還請告知小弟。
這篇博客是自己在學習《java語言程序設計進階篇》時所在筆記,代碼出自書上,最后那個記時的加了計時部分。大三開學在即,希望現在來學java還來得及。
本文出自於博客園蘭幽,轉載請說明出處。