1. 程式人生 > >Java 程序與執行緒 及多執行緒實現(Thread、Runnable、Callable)

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的繼承關係如下圖:
其中箭頭表示繼承關係。
在這裡插入圖片描述