Java 程序與執行緒 及多執行緒實現(Thread、Runnable、Callable)
程序與執行緒的概念
程序: 作業系統中一個程式的執行週期稱為一個程序。(是程式的一次動態執行,它對應著從程式碼載入,執行至執行完畢的一個完整的過程,是一個動態的實體)
windows是一個多程序的作業系統。
執行緒: :一個程式同時執行多個任務。通常,每一個任務就稱為一個執行緒。與程序相比較,執行緒更"輕量級",建立、 撤銷一個執行緒比啟動一個新程序開銷要小的多。沒有程序就沒有執行緒,程序一旦終止,其內的執行緒也將不復存在。
主方法是一個執行緒。
多程序與多執行緒區別: 本質區別在於,每個程序擁有自己的一整套變數,而執行緒則共享資料。共享變數使得執行緒之 間的通訊比程序之間通訊更有效、更方便。
多執行緒表現:一個瀏覽器應用可以同時下載多個圖片、音樂;一個Web伺服器需要同時 處理多個併發的請求。這些都是多執行緒的應用
程序與執行緒比較:
1.與程序相比較,執行緒更加“輕量級”,建立、撤銷一個執行緒比啟動、撤銷一個程序開銷要小的多。一個程序中的所有執行緒共享此程序的所有資源
2.沒有程序就沒有執行緒,程序一旦終止,其內的執行緒也將不復存在
3.程序是作業系統資源排程的基本單位。程序可以獨享資源;執行緒需要依託程序提供的資源,無法獨立申請作業系統資源,是OS任務執行的基本單位。
執行緒狀態
Java多執行緒實現
繼承Thread類實現多執行緒
java.lang.Thread是一個執行緒操作的核心類。新建一個執行緒簡單的方法就是直接繼承Thread類,而後覆寫該類中的 run()方法(就相當於主類中的main方法)
定義執行緒的主體類
class MyThread extends Thread{//執行緒主體類 private String title; public MyThread(String title){ this.title = title; } @Override public void run(){//所有執行緒從此處開始執行 for(int i = 0; i < 10; i++){ System.out.println(this.title + ",i = " + i); } } }
當現在有了執行緒的主體類之後,很自然我們就會想到產生執行緒類的例項化物件而後呼叫run()方法。
觀察呼叫run()方法
public class Test{
public static void main(String[] args){
MyThread myThread1 = new MyThread("abaka");
MyThread myThread2 = new MyThread("abaka1");
MyThread myThread3 = new MyThread("abaka2");
myThread1.run();
myThread2.run();
myThread3.run();
}
}
執行結果:
abaka,i = 0
abaka,i = 1
abaka,i = 2
abaka,i = 3
abaka,i = 4
abaka,i = 5
abaka,i = 6
abaka,i = 7
abaka,i = 8
abaka,i = 9
abaka1,i = 0
abaka1,i = 1
abaka1,i = 2
abaka1,i = 3
abaka1,i = 4
abaka1,i = 5
abaka1,i = 6
abaka1,i = 7
abaka1,i = 8
abaka1,i = 9
abaka2,i = 0
abaka2,i = 1
abaka2,i = 2
abaka2,i = 3
abaka2,i = 4
abaka2,i = 5
abaka2,i = 6
abaka2,i = 7
abaka2,i = 8
abaka2,i = 9
這個時候只是做了一個順序列印,和多執行緒一點關係都沒有。正確啟動多執行緒的方式是呼叫Thread類中的start()方 法。
啟動多執行緒:public synchronized void start()此方法會自動呼叫執行緒的run()方法
正確啟動多執行緒
public class Test{
public static void main(String[] args){
MyThread myThread = new MyThread("abaka");
MyThread myThread1 = new MyThread("abaka1");
MyThread myThread2 = new MyThread("abaka2");
myThread.start();
myThread1.start();
myThread2.start();
}
}
此時再次執行程式碼發現,所有的執行緒物件變味了交替執行。
無論那種方法實現多執行緒,執行緒啟動一定呼叫Thread類提供的start()方法!
執行緒的start方法只能呼叫一次,多次呼叫會java.lang.IllegalThreadStateException
Java執行緒建立路徑:
start(Java方法) -> strat0(JVM) -> 進行資源排程,系統分配(JVM) -> run(java方法)執行執行緒的具體操作任務
Runnable()介面實現多執行緒
Thread類的核心功能是進行執行緒的啟動。如果一個類為了實現多執行緒直接去繼承Thread類就會有但繼承侷限。在 java中又提供有另外一種實現模式:Runnable介面。
Runnable介面:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
利用Runable介面實現執行緒主體類
class MyThread implements Runnable{
private String title;
public MyThread(String title){
this.title = title;
}
@Override
public void run(){
for(int i = 0; i < 5; i++){
System.out.println(this.title + ",i = " + i);
}
}
}
此時MyThread類繼承的不再是Thread類而實現了Runnable介面,雖然解決了單繼 承侷限問題,但是沒有start()方法被繼承了。那麼此時就需要關注Thread類提供的構造方法。
Thread類提供的構造方法
public Thread(Runnable target)
可以接收Runnable介面物件
啟動多執行緒
public class Test{
public static void main(String[] args){
MyThread myThread = new MyThread("abaka");
MyThread myThread1 = new MyThread("abaka1");
MyThread myThread2 = new MyThread("abaka2");
new Thread(myThread).start();
new Thread(myThread1).start();
new Thread(myThread2).start();
}
}
多執行緒的啟動永遠都是Thread類的start()方法
對於此時的Runnable介面物件可以採用匿名內部類或者Lambda表示式來定義。
使用匿名內部類進行Runnable物件建立
public class Test{
public static void main(String[] args){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello abaka");
}
}).start();
}
}
使用Lamdba表示式進行Runnable物件建立
public static void main(String[] args){
nnable runnable = () -> System.out.println("hello abaka");
new Thread(runnable).start();
}
}
繼承Thread類與實現Runnable介面的區別
I.Thread類與自定義執行緒類(實現了Runnable介面)。是一個典型的代理設計模式。Thread類負責輔助真實業務操作(資源排程,建立執行緒並啟動)自定義執行緒類負責真實業務的實現(run方法具體要做啥事)
II.使用Runnable介面實現的多執行緒程式類可以更好的描述共享的概念
使用Thread實現資料共享(產生若干執行緒進行同一資料的處理操作)
class MyThread extends Thread{
private int ticket = 5;
@Override
public void run(){
while(this.ticket > 0){
System.out.println("剩餘票數:" + this.ticket -- );
}
}
}
public class Test{
public static void main(String[] args){
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
此時啟動三個執行緒實現賣票處理。結果變為了賣各自的票
使用Runnable實現共享
class MyThread implements Runnable{
private int ticket = 5;
@Override
public void run(){
while(this.ticket > 0){
System.out.println("剩餘票數: " + this.ticket -- );
}
}
}
public class Test{
public static void main(String[] args){
MyThread myThread = new MyThread();
new Thread(myThread).start();
new Thread(myThread).start();
}
}
執行結果:
剩餘票數: 5
剩餘票數: 4
剩餘票數: 3
剩餘票數: 2
剩餘票數: 1
Runnable實現的多執行緒的程式類可以更好的描述出程式共享的概念
Callable實現多執行緒
從JDK1.5開始追加了新的開發包:java.uti.concurrent。這個開發包主要是進行高併發程式設計使用的,包含很多在高並 發操作中會使用的類。在這個包裡定義有一個新的介面Callable
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Runnable中的run()方法沒有返回值,它的設計也遵循了主方法的設計原則:執行緒開始了就別回頭。但是很多時候需 要一些返回值,例如某些執行緒執行完成後可能帶來一些返回結果,這種情況下就只能利用Callable來實現多執行緒。
使用Callable定義執行緒主體類。
class MyThread implements Callable<String> {
private int ticket = 5;
@Override
public String call() throws Exception{
while(this.ticket > 0){
System.out.println("剩餘票數:" + this.ticket --);
}
return "票賣完";
}
}
啟動並取得多執行緒的執行結果
public class Test{
public static void main(String[] args) throws InterruptedException, ExecutionException{
FutureTask<String> task = new FutureTask<>(new MyThread());
new Thread(task).start();
new Thread(task).start();
System.out.println(task.get());
}
}
其中FutureTask task = new FutureTask<>(new MyThread());這句是因為callable的繼承關係如下圖:
其中箭頭表示繼承關係。