03-Java核心類庫_多執行緒

&再見螢火蟲&發表於2020-10-05

目錄

四,多執行緒

1,執行緒與程式

1.1 執行緒與程式

1.2 執行緒排程

2,同步與非同步&併發與並行

2.1 同步與非同步

2.2 併發與並行

3,繼承Thread

3.1 程式例項

3.2 時序圖

3.3 補充

4,實現Runnable

4.1 使用方法

4.2 實現Runnable與繼承Thread

5,Thread類

5.1 常用構造方法

5.2 常用其他方法

6,設定和獲取執行緒名稱

7,執行緒休眠sleep

8,執行緒的中斷

9,守護執行緒

9.1 概述

9.2 例項

10,執行緒安全1-同步程式碼塊

10.1 執行緒不安全的原因

10.2 程式碼例項

11,執行緒安全2-同步方法

12,執行緒安全3-顯式鎖Lock

13,公平鎖與非公平鎖

13.1 區別

13.2 實現方法

14,多執行緒通訊問題

15,生產者與消費者

15.1 前提條件

15.2 問題一:飯菜的屬性錯亂

15.3 問題二:一次性消費/生產多個菜品

15.4 解決方法

16,執行緒的六種狀態

17,帶返回值的執行緒Callable

17.1 Runnable 與 Callable

 17.2 Callable使用步驟

17.3 常用方法

18,執行緒池概述

18.1 為什麼需要執行緒池

18.2 快取執行緒池

18.3 定長執行緒池

18.4 單執行緒執行緒池

18.5 週期定長執行緒池

19,Lambda表示式

19.1 為什麼要用lambda表示式

19.2 使用例項


四,多執行緒

1,執行緒與程式

1.1 執行緒與程式

程式:

  • 是指一個記憶體中執行的應用程式,每個程式都有一個獨立的記憶體空間

執行緒:

  • 是程式中的一個執行路徑,共享一個記憶體空間,執行緒之間可以自由切換,併發執行. 一個程式最少有一個執行緒
  • 執行緒實際上是在程式基礎之上的進一步劃分,一個程式啟動之後,裡面的若干執行路徑又可以劃分成若干個執行緒

1.2 執行緒排程

目的是為了更合理的利用CPU

分時排程

  • 所有執行緒輪流使用 CPU 的使用權,平均分配每個執行緒佔用 CPU 的時間。

搶佔式排程

  • 優先讓優先順序高的執行緒使用 CPU,如果執行緒的優先順序相同,那麼會隨機選擇一個(執行緒隨機性),Java使用的為搶佔式排程。
  • CPU使用搶佔式排程模式在多個執行緒間進行著高速的切換。對於CPU的一個核新而言,某個時刻,只能執行一個執行緒,而 CPU的在多個執行緒間切換速度相對我們的感覺要快,看上去就是 在同一時刻執行。 其實,多執行緒程式並不能提高程式的執行速度,但能夠提高程式執行效率,讓CPU的 使用率更高。

 

2,同步與非同步&併發與並行

2.1 同步與非同步

同步:排隊執行 , 效率低但是安全.

非同步:同時執行 , 效率高但是資料不安全

2.2 併發與並行

併發:指兩個或多個事件在同一個時間段內發生。

並行:指兩個或多個事件在同一時刻發生(同時發生)。

3,繼承Thread

3.1 程式例項

3.2 時序圖

3.3 補充

每個執行緒都有自己的棧空間,共用一份堆記憶體

4,實現Runnable

4.1 使用方法

另一種實現多執行緒的方法

  • 建立自定義類實現Runnable介面,並重寫run方法;
  • 用自定義類建立一個物件r;
  • 用Thread類建立一個物件t,並將r作為t構造方法的引數;

4.2 實現Runnable與繼承Thread

1)實現Runnable與繼承Thread相比有如下優勢

  • 1,通過建立任務,然後給執行緒分配任務的方式實現多執行緒,更適合多個執行緒同時執行任務的情況;
  • 2,可以避免單繼承所帶來的侷限性(Java允許實現多個介面,但不允許繼承多個父類);
  • 3,任務與執行緒是分離的,提高了程式的健壯性;
  • 4,後期學習的執行緒池技術,接受Runnable型別的任務,不接受Thread型別的執行緒;

 2)Thread也有一定的好處

5,Thread類

5.1 常用構造方法

5.2 常用其他方法

停止執行緒的方法:宣告一個變數,執行緒不斷監控這個變數,一旦變數達到某種條件呼叫return即可

所有的使用者執行緒結束,程式才能結束。守護執行緒是為了守護使用者執行緒,使用者執行緒可以自動結束,所有使用者執行緒結束後,守護執行緒便會像沒有一樣。

6,設定和獲取執行緒名稱

7,執行緒休眠sleep

8,執行緒的中斷

過時的stop方法可以直接中斷執行緒,但是如果執行緒來不及釋放資源,會造成一部分垃圾無法回收;

這裡採用新增中斷標記的方法:呼叫interrupt方法,子執行緒執行時捕獲中斷異常,並在catch塊中,新增處理釋放資源的程式碼;

9,守護執行緒

9.1 概述

執行緒分為守護執行緒和使用者執行緒;

  • 使用者執行緒:當一個程式不包含任何存活的使用者執行緒時,程式結束;
  • 守護執行緒:守護使用者執行緒,當最後一個使用者執行緒結束後,所有守護執行緒自動死亡;

直接建立的都是使用者執行緒;

設定守護執行緒:執行緒物件.setDaemon(true);

9.2 例項

1)不設定守護執行緒

2)設定為守護執行緒

10,執行緒安全1-同步程式碼塊

10.1 執行緒不安全的原因

多個執行緒爭搶同一個資料,使得資料在判斷使用時出現不一致的情況。解決方法,保證一段資料同時只能被一個執行緒使用(排隊使用)。

解決方案一:同步程式碼塊

格式:

synchronize(鎖物件){


}

10.2 程式碼例項

1,不加鎖

2,加鎖後

package com.kaikeba;

import javax.xml.namespace.QName;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.Scanner;
import java.util.SimpleTimeZone;

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        //執行緒不安全
        //解決方案1  同步程式碼塊
        //格式:synchronized(鎖物件){
        //
        //
        //      }
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable{
        //總票數
        private int count = 10;
        private Object o = new Object();
        @Override
        public void run() {
            //Object o = new Object();    //這裡不是同一把鎖,所以鎖不住
            while (true) {
                synchronized (o) {
                    if (count > 0) {
                        //賣票
                        System.out.println("正在準備賣票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"賣票結束,餘票:" + count);
                    }else {
                        break;
                    }

                }
            }
        }
    }
}

11,執行緒安全2-同步方法

同步程式碼塊粒度較細,可以給一行程式碼單獨加鎖,同步方法顧名思義,是給方法加鎖;

package com.kaikeba;

import javax.xml.namespace.QName;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.Scanner;
import java.util.SimpleTimeZone;

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        //執行緒不安全
        //解決方案2  同步方法
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable{
        //總票數
        private int count = 10;
        @Override
        public void run() {

            while (true) {
                boolean flag = sale();
                if(!flag){
                    break;
                }
            }
        }
        public synchronized boolean sale(){
            if (count > 0) {
                //賣票
                System.out.println("正在準備賣票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"賣票結束,餘票:" + count);
                return true;
            }
            return false;

        }
    }
}

給方法上鎖,對應的鎖物件就是this, 如果是靜態修飾方法的話,鎖物件為類名.class(比如這裡sale方法若被修飾為靜態方法的話,鎖物件為Ticket.class,也就是位元組碼檔案物件)

針對以上程式碼來說 ,鎖物件如下:

12,執行緒安全3-顯式鎖Lock

同步方法和同步程式碼塊都屬於隱式鎖,顯式鎖則是程式設計師手動加鎖、解鎖;

package thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


//同步程式碼塊和同步方法都屬於隱式鎖
//執行緒同步lock

public class Demo10 {
    public static void main(String[] args) {
        Object o = new Object();
        //執行緒不安全
        //解決方案1   顯示鎖  Lock  子類 ReentrantLock

        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //總票數
        private int count = 10;
        //引數為true表示公平鎖    預設是false 不是公平鎖
        private Lock l = new ReentrantLock(true);
        @Override
        public void run() {
            while (true) {
                l.lock();
                    if (count > 0) {
                        //賣票
                        System.out.println("正在準備賣票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"賣票結束,餘票:" + count);
                    }else {
                        break;
                    }
                    l.unlock();
            }
        }
    }
}

13,公平鎖與非公平鎖

13.1 區別

公平鎖:先來先得,遵循排隊;

非公平鎖:大家一起搶(同步程式碼塊,同步方法,顯式鎖都屬於非公平鎖);

13.2 實現方法

在顯式鎖例項化時,傳入引數true()

14,多執行緒通訊問題

主要藉助於wait和notify函式實現

15,生產者與消費者

15.1 前提條件

廚師cook為生產者執行緒,服務員waiter為消費者執行緒,食物為生產與消費的物品;

假設目前只有一個廚師,一個服務員,一個盤子。理想狀態是:廚師生產一份飯菜,服務員端走一份,且飯菜的屬性未發生錯亂;

廚師可以製作兩種口味的飯菜,製作100次;

服務員可以端走飯菜100次;

15.2 問題一:飯菜的屬性錯亂

1)實驗程式碼

package com.kaikeba;

public class Demo1 {
    public static void main(String[] args) {
        //多執行緒通訊    生產者與消費者問題
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }
    //廚師
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if(i%2==0){// 設計兩種菜色
                    f.setNameAndTaste("老乾媽小米粥","香辣味");
                }else {
                    f.setNameAndTaste("煎餅果子","甜辣味");
                }
            }
        }
    }
    //服務員
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;
        public void setNameAndTaste(String name,String taste){// 生產
            this.name = name;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.taste = taste;

        }
        public void get(){  // 消費
            System.out.println("服務員端走的菜的名稱是:"+name+",味道是:"+taste);
        }
    }
}

2)錯誤現象 

3)錯誤原因

15.3 問題二:一次性消費/生產多個菜品

1)實驗程式碼

為了防止在生產過程中setNameAndTaste出現時間片切換,可以用synchronized修飾此方法;

public synchronized void setNameAndTaste(String name,String taste){// 生產
            this.name = name;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.taste = taste;

        }
        public synchronized void get(){  // 消費
            System.out.println("服務員端走的菜的名稱是:"+name+",味道是:"+taste);
        }

2)執行效果

3)原因分析

synchronized只是確保了方法內部不會發生執行緒切換,但並不能保證生產一個消費一個的邏輯關係;

15.4 解決方法

廚師做完飯後喊醒服務員,自己睡著。服務員送完飯後喊醒廚師,自己睡著;

主要修改的部分為setNameAndTaste與get方法:

public synchronized void setNameAndTaste(String name,String taste){// 生產
            if(flag) {
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;        // 表示飯菜生產完畢
                this.notifyAll();    // 叫醒服務員
                try {
                    this.wait();     // 睡著
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public synchronized void get(){  // 消費
            if(!flag){
                System.out.println("服務員端走的菜的名稱是:"+name+",味道是:"+taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }

可以看出飯菜是交替產生並消費的; 

 

16,執行緒的六種狀態

17,帶返回值的執行緒Callable

新的建立執行緒的方式。之前的建立執行緒方式:實現Thread的子類、實現Runnable介面,可以看成是和主執行緒併發執行的;

這裡要講的執行緒更像是主執行緒指派的一個任務,主執行緒可以獲得其返回值;

17.1 Runnable 與 Callable

1)介面定義

介面定義 

//Callable介面 
public interface Callable<V> { 
    V call() throws Exception; 
}

//Runnable介面 
public interface Runnable { 
    public abstract void run(); 
}

2)相同點

都是介面

都可以編寫多執行緒程式

都採用Thread.start()啟動執行緒

3)不同點

Runnable沒有返回值;Callable可以返回執行結果

Callable介面的call()允許丟擲異常;Runnable的run()不能丟擲

 17.2 Callable使用步驟

1. 編寫類實現Callable介面 , 實現call方法 

class XXX implements Callable<T> { 
    @Override 
    public <T> call() throws Exception { 
        return T; 
    } 
} 

2. 建立FutureTask物件 , 並傳入第一步編寫的Callable類物件 

FutureTask<Integer> future = new FutureTask<>(callable); 

3. 通過Thread,啟動執行緒 

new Thread(future).start();

17.3 常用方法

1)方法介紹 

 2)程式碼示例

package com.kaikeba;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class Demo1 {
    public static void main(String[] args) {
        Callable<Integer> c = new MyCallable();
        FutureTask<Integer> f = new FutureTask<>(c);
        new Thread(f).start();
        for(int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
    static class MyCallable implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
//            Thread.sleep(100);// 睡眠過後 給出結果
            for(int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + i);
            }
            return 100;
        }
    }

}

未使用get方法時,主執行緒和另一個執行緒交替執行

使用get方法

 

18,執行緒池概述

18.1 為什麼需要執行緒池

如果併發的執行緒數量很多,並且每個執行緒都是執行一個時間很短的任務就結束了,這樣頻繁建立執行緒 就會大大降低 系統的效率,因為頻繁建立執行緒和銷燬執行緒需要時間.

執行緒池就是一個容納多個執行緒的容 器,池中的執行緒可以反覆使用,省去了頻繁建立執行緒物件的操作,節省了大量的時間和資源。

Java中的四種執行緒池(物件均為ExecutorService) 

18.2 快取執行緒池

1)概述

/*** 快取執行緒池. 
* (長度無限制) 
* 執行流程: 
* 1. 判斷執行緒池是否存在空閒執行緒 
* 2. 存在則使用 
* 3. 不存在,則建立執行緒 並放入執行緒池, 然後使用 
*/ 
ExecutorService service = Executors.newCachedThreadPool(); 
//向執行緒池中 加入 新的任務 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("執行緒的名稱:"+Thread.currentThread().getName()); 
    } 
}); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("執行緒的名稱:"+Thread.currentThread().getName()); 
    } 
}); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("執行緒的名稱:"+Thread.currentThread().getName()); 
    } 
});

2)程式碼示例

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*** 快取執行緒池.
         * (長度無限制)
         * 執行流程:
         * 1. 判斷執行緒池是否存在空閒執行緒
         * 2. 存在則使用
         * 3. 不存在,則建立執行緒 並放入執行緒池, 然後使用
         */
        ExecutorService service = Executors.newCachedThreadPool();
        // 指揮執行緒池執行新的任務
        service.execute(new Runnable() {// 匿名內部類
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "滴滴滴");
            }
        });
        service.execute(new Runnable() {// 匿名內部類
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "滴滴滴");
            }
        });
        service.execute(new Runnable() {// 匿名內部類
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "滴滴滴");
            }
        });

    }
}

睡眠一段時間後,新增新的任務,檢視是否能利用執行緒池中已存在的執行緒:

18.3 定長執行緒池

1)概述

/**
* 定長執行緒池. 
* (長度是指定的數值) 
* 執行流程:
* 1. 判斷執行緒池是否存在空閒執行緒 
* 2. 存在則使用 
* 3. 不存在空閒執行緒,且執行緒池未滿的情況下,則建立執行緒 並放入執行緒池, 然後使用 
* 4. 不存在空閒執行緒,且執行緒池已滿的情況下,則等待執行緒池存在空閒執行緒 
*/ 
ExecutorService service = Executors.newFixedThreadPool(2); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("執行緒的名稱:"+Thread.currentThread().getName()); 
    } 
}); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("執行緒的名稱:"+Thread.currentThread().getName()); 
    } 
});

2)程式碼示例

設定執行緒池大小為2,即執行緒池中最多隻允許存在兩個執行緒;

前兩個執行緒執行時,均sleep三秒鐘;

第三個任務由於執行緒池已滿,不能開闢新的執行緒,所以必須等執行緒池中有空閒執行緒出現才可以執行;

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    /*定長執行緒池
    長度是指定的執行緒池
    加入任務後的執行流程
        1 判斷執行緒池是否存在空閒執行緒
        2 存在則使用
        3 不存在空閒執行緒  且執行緒池未滿的情況下  則建立執行緒  並放入執行緒池中  然後使用
        4 不存在空閒執行緒  且執行緒池已滿的情況下  則等待執行緒池的空閒執行緒
    **/
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
            }
        });
    }
}

 

18.4 單執行緒執行緒池

1)概述

效果與定長執行緒池 建立時傳入數值1 效果一致. 
/**
* 單執行緒執行緒池. 
* 執行流程: 
* 1. 判斷執行緒池 的那個執行緒 是否空閒 
* 2. 空閒則使用 
* 3. 不空閒,則等待 池中的單個執行緒空閒後 使用 
*/ 
ExecutorService service = Executors.newSingleThreadExecutor(); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("執行緒的名稱:"+Thread.currentThread().getName()); 
    } 
}); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("執行緒的名稱:"+Thread.currentThread().getName()); 
    } 
});

2)程式碼示例

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    /*單執行緒執行緒池
    執行流程
        1 判斷執行緒池的那個執行緒是否空閒
        2 空閒則使用
        3 不空閒則等待它空閒後再使用
    **/
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
            }
        });
    }
}

 

18.5 週期定長執行緒池

1)概述

public static void main(String[] args) { 
    /**
    * 週期任務 定長執行緒池. 
    * 執行流程: 
    * 1. 判斷執行緒池是否存在空閒執行緒 
    * 2. 存在則使用 
    * 3. 不存在空閒執行緒,且執行緒池未滿的情況下,則建立執行緒 並放入執行緒池, 然後使用 
    * 4. 不存在空閒執行緒,且執行緒池已滿的情況下,則等待執行緒池存在空閒執行緒 
    *
    * 週期性任務執行時: 
    * 定時執行, 當某個時機觸發時, 自動執行某任務 .
    */ 
    ScheduledExecutorService service = Executors.newScheduledThreadPool(2); 

    /**
    * 定時執行 
    * 引數1. runnable型別的任務 
    * 引數2. 時長數字 
    * 引數3. 時長數字的單位 
    */ 

    /*
    service.schedule(new Runnable() { 
        @Override 
        public void run() { 
            System.out.println("倆人相視一笑~ 嘿嘿嘿"); 
        } 
    },5,TimeUnit.SECONDS); 
    */

    /**
    * 週期執行 
    * 引數1. runnable型別的任務 
    * 引數2. 時長數字(延遲執行的時長) 
    * 引數3. 週期時長(每次執行的間隔時間) 
    * 引數4. 時長數字的單位 
    */ 
    service.scheduleAtFixedRate(new Runnable() { 
        @Override 
        public void run() { 
            System.out.println("倆人相視一笑~ 嘿嘿嘿"); 
        } 
    },5,2,TimeUnit.SECONDS); 
}

2)程式碼示例

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    /*週期任務  定長執行緒池
    執行流程
        1 判斷執行緒池是否存在空閒執行緒
        2 存在則使用
        3 不存在空閒執行緒  且執行緒池未滿的情況下  則建立執行緒  並放入執行緒池中  然後使用
        4 不存在空閒執行緒  且執行緒池已滿的情況下  則等待執行緒池的空閒執行緒

        週期性任務執行時
                定時執行 當某個任務觸發時  自動執行某任務
    **/
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        //定時執行一次
        //引數1:定時執行的任務
        //引數2:時長數字
        //引數3:2的時間單位    Timeunit的常量指定
       /* scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
            }
        },5, TimeUnit.SECONDS);      //5秒鐘後執行*/

        /*
        週期性執行任務
            引數1:任務
            引數2:延遲時長數字(第一次執行延遲的時間)
            引數3:週期時長數字(每隔多久執行一次)
            引數4:時長數字的單位
        * **/
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
            }
        },5,1,TimeUnit.SECONDS);
    }
}

19,Lambda表示式

19.1 為什麼要用lambda表示式

對於某些應用場景,我們更注重於結果,如果能用一個方法解決,那麼通過建立物件、呼叫方法的方式可能會更加繁瑣;

1)冗餘的Runnable方法

public class Demo1 {
    /**
     * lambda表示式
     * 函數語言程式設計思想(注重結果,而物件導向則是通過建立物件,解決問題)
     * @param args
     */
    public static void main(String[] args) {
        // 冗餘的Runnable程式碼
        Runnable r = new MyRunnable();
        Thread t = new Thread(r);
        t.start();  // 寫了這麼多 只為了完成一個簡單的任務
    }
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            System.out.println("任務完成!");
        }
    }
}

2)通過匿名內部類簡化程式碼

public class Demo1 {
    /**
     * lambda表示式
     * 函數語言程式設計思想(注重結果,而物件導向則是通過建立物件,解決問題)
     * @param args
     */
    public static void main(String[] args) {
        // 冗餘的Runnable程式碼
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("任務完成!");
            }
        });
        t.start();  // 寫了這麼多 只為了完成一個簡單的任務
    }
}

19.2 使用例項

1)不使用lambda

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    /**
     * lambda表示式
     * 函數語言程式設計思想(注重結果,而物件導向則是通過建立物件,解決問題)
     * @param args
     */
    public static void main(String[] args) {
        print(new MyMath() {
            @Override
            public int sum(int x, int y) {
                return x + y;
            }
        }, 100, 200);
    }
    public static void print(MyMath m, int x, int y){
        int num = m.sum(x, y);
        System.out.println(num);
    }
    static  interface MyMath{
        int sum(int x, int y);
    }
}

2)使用lambda

不需要實現介面、例項化物件;

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    /**
     * lambda表示式
     * 函數語言程式設計思想(注重結果,而物件導向則是通過建立物件,解決問題)
     * @param args
     */
    public static void main(String[] args) {
        print((int x, int y) -> {
                return x + y;
            }, 100, 200);
    }
    public static void print(MyMath m, int x, int y){
        int num = m.sum(x, y);
        System.out.println(num);
    }
    static  interface MyMath{
        int sum(int x, int y);
    }
}

 

相關文章