1. 程式人生 > >18.Java語言執行緒池和Lambda表示式

18.Java語言執行緒池和Lambda表示式

執行緒等待喚醒機制

1.執行緒間的通訊:

       一個程式完成某個任務,需要多個執行緒協調,就需要執行緒之間存在“通訊”,比如生產者和消費者,只有生產了才能被消費。當生產者生產完成才能告知消費者可以消費,那麼告知的過程就是執行緒間的通訊。

2.等待與喚醒機制:

              1)."等待與喚醒機制”就是“執行緒間通訊”的一種體現。

              2).工作形式:

                     1).一個執行緒做一些“準備性工作”。

          2).另一個執行緒做正常的工作。由於兩個執行緒是“無序的”,很有可能“第二個執行緒工作時,第一個執行緒的準備工作還沒

           有做好,這時就需要第二個執行緒要主動“等待”,然後第一個執行緒進入開始做準備,準備工作做好後,再喚醒第二個

            執行緒開始正常工作。

喚醒機制_生產者與消費者

                     執行緒通訊_包子鋪 -----生產者生產包子,放在包子鋪,消費者到包子鋪取包子,只有生產者生產完包子通知消費者,消費者才

                                             可以到包子鋪獲取包子,否則只能無限等待

包子鋪類:

import java.util.ArrayList;
import java.util.List;

public class BaoZiPu{
    List<String> list = new ArrayList<>();
    Object obj = new Object();
    public void setBaozi(){
        synchronized (obj){
            list.add("包子");
            obj.notifyAll();
        }
    }
    public String getBao() throws InterruptedException {
        synchronized (obj){
            if(list.size() == 0){
                System.out.println("訪問的執行緒需要等待....");
                obj.wait();
                System.out.println("訪問的執行緒被喚醒......");
            }
            String s = list.get(0);
            list.remove(s);
            return s;

        }
        }
}

獲取包子 

public class GetBaozi extends Thread{
    BaoZiPu baoZiPu;

    public GetBaozi(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while (true){
            try {
                System.out.println(baoZiPu.getBao());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

生產包子

public class SetBaozi extends Thread {
    BaoZiPu baoZiPu;

    public SetBaozi(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while (true) {
            baoZiPu.setBaozi();
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 測試類:

public class Demo {
    public static void main(String[] args) {
        BaoZiPu baoZiPu = new BaoZiPu();
        GetBaozi getBaozi = new GetBaozi(baoZiPu);
        SetBaozi setBaozi = new SetBaozi(baoZiPu);
        getBaozi.start();
        setBaozi.start();
    }
}

執行緒池

儲存了若干多的“執行緒物件”的一個“容器”。 執行緒池類:ExecutorService這個執行緒池就可以快取大量的執行緒物件,並可以反覆的重用它們。

1.執行緒池思想概述

1).對於一個執行緒物件“只能啟動一次”,若想第二次再用,就需要再次建立一個執行緒物件,但如果建立執行緒物件很耗時,這

樣如果程式中要反覆的使用同一個執行緒,整個程式的效率就會很低。

2).執行緒池的思想:在程式啟動時,會先建立若干多的執行緒物件,並存儲到一個容器中,作為“執行緒池”。如果有需要時,取

出一個執行緒物件,並執行它。執行完畢,執行緒池會回收這個執行緒物件,如果再次需要,可以再次取出,並執行它。所以,

執行緒池中的執行緒物件是“可以反覆重用”的。這樣就避免的反覆的建立執行緒物件,從而提高程式的執行效率。

2.執行緒池的使用

Java裡面執行緒池的頂級介面是 java.util.concurrent.Executor ,但是嚴格意義上講Executor 並不是一個執行緒池,而只是一

個執行執行緒的工具。真正的執行緒池介面是java.util.concurrent.ExecutorService

Executors類中有個建立執行緒池的方法如下:

a).public static ExecutorService newFixedThreadPool(int nThreads) :返回執行緒池物件。(建立的是有界執行緒池,也就是池

中的執行緒個數可以指定最大數量)

b).public Future<?> submit(Runnable task) :獲取執行緒池中的某一個執行緒物件,並執行

3.執行緒池的特點

如果建立一個執行緒需要五秒鐘,不用執行緒池,每建立一個執行緒就需要五秒,用了執行緒池,因為執行緒池執行後,會將此執行緒

物件"快取",可以重用,那麼再次建立就不需要時間。

示例:

public class MyThread extends Thread {
    public MyThread() {
        for (int i = 0; i < 5; i++) {
            System.out.println("等待中..." + (i + 1) + "秒");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    @Override
    public void run() {
        System.out.println("執行緒執行啦");
    }

}

測試類:

public class Demo {

    public static void main(String[] args) {

        MyThread myThread = new MyThread();

        myThread.start();

        myThread = new MyThread();

        myThread.start();
    }

}

Lambda表示式

1.冗餘的Runnable程式碼

方式一是建立Runnable的實現類的物件,通過Thread的建構函式建立執行緒。

方式二是用Runnable的匿名內部類建立執行緒。

public class Demo {
    public static void main(String[] args) {
        //1.方式一:製作Runnable子類的方式
         MyRunnable myRunnable = new MyRunnable();
        Thread t = new Thread(myRunnable);
        t.start();
        //2.方式二:匿名內部類的方式
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("i = " + i);
                }
            }
        });
        t2.start();
    }
}

2.Lambda表示式的寫法

3.程式設計思想轉換及函數語言程式設計思想概述

1).程式設計思想轉換:將“以什麼形式做”,轉換為“怎樣做”。

2).“函式式”程式設計思想:當呼叫的方法需要一個“介面”型別時,就可以考慮直接傳入一個代替的“函式(方法)”即可,其它無用的語句可以省略。

4.Lambda表示式的使用

       1).使用前提:具備以下條件,才可以使用Lambda。

              1).首先需要的是一個“介面型別”;

              2).而且這個介面中有,且只有一個“抽象方法”--函式式介面;

       2).標準格式(三部分)

              第一部分:一對小括號--形參;

              第二部分:一個右箭頭:->

              第三部分:一對大括號--方法體;

       3).標準格式的的寫法

              無參       ()->{//方法體}

              有參       (int a,int b)->{//方法體}

              方法體內按照正常程式碼格式

5.Lambda表示式的省略格式和原則

       1).示例:

//Lambda表示式--完整格式

fun((int x, int y) -> {return x + y;}, 10, 20);

//簡寫的Lambda

fun((x, y) -> x + y, 10, 20);

       2).省略規則:

              1).形參:Lambda中的"形參型別”都可以省略;

                     fun((x) -> {return x * x;});

              2).形參:如果只有一個形參,形參型別和小括號都可以省略

                        (要省略小括號,必須省略資料型別)

                     fun(x -> {return x * x;});

              3).方法體:如果方法體中只有一條語句,可以應用省略規則;

                     A).有返回值:可以省略大括號、return關鍵字、語句後的分號

                                   (要省全省,要用全用)

                            fun(x -> x * x);

                     B).無返回值:可以省略大括號、語句後的分號;

                                   (要省全省,要用全用)

                            fun(x -> System.out.println("x = " + x));