1. 程式人生 > 實用技巧 >多執行緒,,,,,執行緒池

多執行緒,,,,,執行緒池

多執行緒

1. Thread類

建立新執行執行緒有兩種方法。

l 一種方法是將類宣告為 Thread 的子類。該子類應重寫 Thread 類的 run 方法。建立物件,開啟執行緒。run方法相當於其他執行緒的main方法。

l 另一種方法是宣告一個實現 Runnable 介面的類。該類然後實現 run 方法。然後建立Runnable的子類物件,傳入到某個執行緒的構造方法中,開啟執行緒。

繼承Thread,重寫run方法

public class MyThread extends Thread{

public void run() {
    
    
//重寫,描述執行緒任務 for(int i=0;i<100;i++){ System.out.println("MyThread:"+i); } } }

測試

public class Demo02 {//多執行緒程式同時進行
public static void main(String[] args) {
    //建立執行緒物件
    MyThread my=new MyThread();
    my.start();//開啟執行緒,start呼叫的是Thread類中的run方法
    for(int i=0;i<100;i++){
        System.
out.println("main:"+i); } } }

兩個迴圈同時進行,交叉列印,因為調start方法時會開一個新棧,兩個棧交替執行

獲取執行緒名稱

l Thread.currentThread()獲取當前執行緒物件

l Thread.currentThread().getName();獲取當前執行緒物件的名稱

public class Demo02 {//多執行緒程式同時進行
public static void main(String[] args) {
    //建立執行緒物件
    MyThread my=new MyThread();
    
    my.start();
//開啟執行緒 for(int i=0;i<100;i++){ //先獲得執行緒物件,再獲取執行緒名稱 System.out.println(Thread.currentThread().getName()+":"+i); } } }
public class MyThread extends Thread{

public void run() {
    
    //重寫,描述執行緒任務
    for(int i=0;i<100;i++){
        System.out.println(Thread.currentThread().getName()+":"+i);//獲取執行緒名稱
    }
}
}

建立執行緒方式—實現Runnable介面

Runnable介面用來指定每個執行緒要執行的任務。包含了一個 run 的無引數抽象方法,需要由介面實現類重寫該方法。

l Thread類構造方法

建立執行緒的步驟。

1、定義類實現Runnable介面。

2、覆蓋介面中的run方法。。

3、建立Thread類的物件

4、將Runnable介面的子類物件作為引數傳遞給Thread類的建構函式。

5、呼叫Thread類的start方法開啟執行緒。

public class Demo01 {
public static void main(String[] args) {
    //建立執行緒執行目標物件
    MyRunnable my=new MyRunnable();
    //將Runnable介面的子類物件作為引數傳遞給Thread類的建構函式
    Thread th=new Thread(my);
    //開啟執行緒
    th.start();
    //主執行緒任務
    for(int i=0;i<100;i++){
        System.out.println(Thread.currentThread().getName()+":"+i);
    }
}
}
public class MyRunnable implements Runnable {
    //定義執行緒要執行的run方法邏輯
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }


}

實現Runnable的原理

實現Runnable介面,避免了繼承Thread類的單繼承侷限性。覆蓋Runnable介面中的run方法,將執行緒任務程式碼定義到run方法中。

建立Thread類的物件,只有建立Thread類的物件才可以建立執行緒。執行緒任務已被封裝到Runnable介面的run方法中,而這個run方法所屬於Runnable介面的子類物件,所以將這個子類物件作為引數傳遞給Thread的建構函式,這樣,執行緒物件建立時就可以明確要執行的執行緒的任務。

實現Runnable的好處

現Runnable介面避免了單繼承的侷限性,所以較為常用。實現Runnable介面的方式,更加的符合面向物件,執行緒分為兩部分,一部分執行緒物件,一部分執行緒任務。

執行緒的匿名內部類使用

匿名內部類格式:new父類或介面(){ 重寫父類方法 }

使用執行緒的內匿名內部類方式,可以方便的實現每個執行緒執行不同的執行緒任務操作。

public class Demo02 {
public static void main(String[] args) {
    //繼承Thread類的匿名內部類物件
    //new父類(){重寫父類方法}
    Thread t=new Thread(){//多型,得到父類的子類物件
        public void run() {
            for(int i=1;i<10;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
        };
        t.start();
        //實現runable介面
        //建立執行緒任務物件
        Runnable my=new Runnable(){
            public void run() {
                for(int i=0;i<10;i++){
                    System.out.println(Thread.currentThread().getName()+":::"+i);
                }
            };
        };
        //建立執行緒物件,交替執行
        new Thread(my).start();
    }
}

執行緒池

執行緒池概念

執行緒池,其實就是一個容納多個執行緒的容器,其中的執行緒可以反覆使用,省去了頻繁建立執行緒物件的操作,無需反覆建立執行緒而消耗過多資源。

使用執行緒池方式--Runnable介面

l Executors:執行緒池建立工廠類

public static ExecutorService newFixedThreadPool(int nThreads):返回執行緒池物件

l ExecutorService:執行緒池類

Future<?> submit(Runnabletask):獲取執行緒池中的某一個執行緒物件,並執行

l Future介面:用來記錄執行緒任務執行完畢後產生的結果。執行緒池建立與使用

l 使用執行緒池中執行緒物件的步驟:

建立執行緒池物件

建立Runnable介面子類物件

提交Runnable介面子類物件

關閉執行緒池

public class Demo03 {
public static void main(String[] args) {
    //獲取執行緒池物件
    ExecutorService es=Executors.newFixedThreadPool(2);
    //建立執行緒任務物件
    MyRunnable r=new MyRunnable();
    //執行執行緒任務,如果執行緒任務大於執行緒數,就排隊執行
    es.submit(r);
    es.submit(r);
    //銷燬執行緒池
    es.shutdown();
}
}

l Runnable介面實現類

public class MyRunnable implements Runnable {
    //定義執行緒要執行的run方法邏輯
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }


}

使用執行緒池方式—Callable介面

l Callable介面:與Runnable介面功能相似,用來指定執行緒的任務。其中的call()方法,用來返回執行緒任務執行完畢後的結果,call方法可丟擲異常。

l ExecutorService:執行緒池類

<T> Future<T> submit(Callable<T>task):獲取執行緒池中的某一個執行緒物件,並執行執行緒中的call()方法

l Future介面:用來記錄執行緒任務執行完畢後產生的結果。執行緒池建立與使用

l 使用執行緒池中執行緒物件的步驟:

建立執行緒池物件

建立Callable介面子類物件

提交Callable介面子類物件

關閉執行緒池

public class Demo04 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
    //建立執行緒池物件
    ExecutorService es=Executors.newFixedThreadPool(2);
    //建立Callable物件
    MyCallable c=new MyCallable();
    //執行執行緒任務
    //從執行緒池中獲取執行緒物件,然後呼叫MyRunnable中的run()
    Future<String> f=es.submit(c);
    //注意:submit方法呼叫結束後,程式並不終止,
    //是因為執行緒池控制了執行緒的關閉。將使用完的執行緒又歸還到了執行緒池中
    //獲取返回值
    String mes=f.get();
    System.out.println(mes);
    es.shutdown();
}
}

l Callable介面實現類,call方法可丟擲異常、返回執行緒任務執行完畢後的結果

public class MyCallable implements Callable<String>{

    
    public String call() throws Exception {
        
        return "abc";
    }

}

練習

用兩條執行緒分別計算1-50的和 和1-100的和,將結果返回

public class Demo01 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
    //用兩條執行緒分別計算1-50的和 和1-100的和,將結果返回
    //獲取執行緒池物件
    ExecutorService es=Executors.newFixedThreadPool(2);
    //建立執行緒任務
    Call c1=new Call(100);
    Call c2=new Call(50);
    //執行
    Future<Integer> f1=es.submit(c1);
    Future<Integer> f2=es.submit(c2);
    //獲取返回值
    System.out.println(f1.get());
    System.out.println(f2.get());
    //銷燬執行緒池
    es.shutdown();
    
    
}
}

public class Call implements Callable<Integer>{
    private int num;
    public Call(int num){
        this.num=num;
    }
    public Integer call() throws Exception {
        int sum=0;
        for(int i=1;i<=num;i++){
            sum=sum+i;
        }
        return sum;
    }

}