線程和進程的基本概念
進程和線程是動態的概念。
進程是 “執行中的程序“,是一個動詞,而程序是一個名詞,進程運行中程序的”代碼“,而且還有自己的計數器,寄存器,它會向系統申請系統資源。
線程是進程中的一個控制流。一個程序可能可能包含多個任務並發運行,而線程就是指一個任務重頭到尾的執行流。
說的在簡單點,線程是執行中的任務,一個程序包含多個任務。
多線程
單處理器中,為提高處理器的使用率(最終目標),使得程序在進行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還來得及。
本文出自於博客園蘭幽,轉載請說明出處。





