1. 程式人生 > >Java之線程池和Lambda表達式

Java之線程池和Lambda表達式

例子 少見 sin 防止 單例模式 多線程 immediate creat 問題

線程池和lambda表達式

學習線程池和lambda表達式的理解

補充一個知識點(單例設計模式)

在多線程中,我們只需要一個任務類,為了防止創建多個任務類,這個時候就需要用到單例模式,單例模式有兩種設計:

  1. 延遲加載(懶漢式)
    • 私有構造方法
    • 創建本類對象,但不初始化
    • 創建靜態方法進行初始化對象並返回
  2. 立即加載(餓漢式)
    • 私有構造方法
    • 創建本類的對象並初始化(私有的)
    • 創建靜態方法獲取本類對象

下面用代碼做個實例:

package com.wzlove.single;

/**
 * 延遲加載(懶漢式)
 *  1.私有構造方法
 *  2.創建本類對象,但不初始化
 *  3.創建靜態方法進行初始化對象並返回
 *
 *  優點:
 *      使用到類的對象才會加載,不消耗內存
 *  缺點:
 *      可能會出現線程安全問題,但是可以使用同步代碼塊消除這個安全問題
 * @author WZLOVE
 * @create 2018-07-19 10:36
 */
public class DeplayedSingle {

    // 私有構造方法
    private DeplayedSingle(){}

    // 創建本類對象,不初始化
    private  static DeplayedSingle instance ;

    // 靜態方法初始化
    public static DeplayedSingle getInstance(){
        synchronized (DeplayedSingle.class){
            if(instance == null){
                instance = new DeplayedSingle();
            }
        }
        return instance;
    }
}



package com.wzlove.single;

/**
 * 立即加載(餓漢式)
 *  1.私有構造方法
 *  2.創建本類的對象並初始化(私有的)
 *  3.創建靜態方法獲取本類對象
 *
 *  優點:
 *      保證的線程的安全,沒有線程安全問題
 *  缺點:
 *      使用類就會加載,比較消耗內存
 * @author WZLOVE
 * @create 2018-07-19 10:37
 */
public class ImmediateSingle {

    private ImmediateSingle(){}

    private static ImmediateSingle instance = new ImmediateSingle();

    public static ImmediateSingle getInstance(){
        return instance;
    }
}

線程池

概述

線程池其實就是一個容納多個線程的容器,其中的線程可以反復使用,省去了頻繁創建線程對象的操作,
無需反復創建線程而消耗過多資源。作用是節省資源,原理是容器.

使用線程池的好處是減少在創建和銷毀線程上所花的時間以及系統資源的開銷,解決資源不足的問題.如果不使用線程池,
有可能造成系統創建大量同類線程而導致消耗完內存或者"過度切換"的問題.

創建線程池:

  • public static ExecutorService newFixedThreadPool(int nThreads) :返回線程池對象。(創建的是有界線
    程池,也就是池中的線程個數可以指定最大數量)
  • public Future<?> submit(Runnable task) :獲取線程池中的某一個線程對象,並執行
  • Future接口:用來記錄線程任務執行完畢後產生的結果。線程池創建與使用。

步驟:

  • 使用線程池的工廠類Executors裏面提供靜態方法newFixedThreadPool生產一個指定線程數量的線程池
  • 創建一個類,實現Runnable接口,重寫run方法,設置線程任務
  • 調用ExecutorService中的方法submit,傳遞線程任務(實現類),開啟線程(前提是線程池中有線程可用),執行run方法
  • 調用ExecutorService中的shutdown方法銷毀線程池(不建議執行)

補充線程的創建

創建線程的第三種方式(這種方式很少見)

package com.wzlove.executor;

import java.util.ArrayList;
import java.util.concurrent.Callable;

/**
 * 線程的第三種創建方法
 *  Callable有返回值並且可以拋出異常
 * @author WZLOVE
 * @create 2018-07-18 14:14
 */
public class CallableImpl implements Callable<String> {

    private static ArrayList<String> arrayList = new ArrayList<>();

    static{
        arrayList.add("1");
    }

    @Override
    public String call() throws Exception {
        return Thread.currentThread().getName();
    }
}



package com.wzlove.executor;

import java.util.concurrent.*;

/**
 * 測試線程的第三中創建方式
 *
 * @author WZLOVE
 * @create 2018-07-18 14:14
 */
public class Demo {

    public static void main(String[] args) {
        // 創建實現Callable的實現類對象
        Callable<String> callable = new CallableImpl();
        // 創建FutureTask,並傳遞Callable接口的實現類對象
        FutureTask task = new FutureTask(callable);
        // 創建線程池對象
        // ExecutorService executor = Executors.newSingleThreadExecutor();
        ExecutorService executor = Executors.newFixedThreadPool(3);
        executor.submit(callable);
        // 執行線程
        executor.execute(task);

        try {
            System.out.println(task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }


}

Lambda表達式

lambda體現的是一種函數式編程的思想, 它強調的是做什麽,而不是以什麽形式做。

使用前提

  • 使用Lambda必須具有接口,且要求接口中有且僅有一個抽象方法。無論是JDK內置的Runnable 、Comparator 接口還是自定義的接口,只有當接口中的抽象方法存在且唯一時,才可以使用Lambda。
  • 使用Lambda必須具有上下文推斷。也就是方法的參數或局部變量類型必須為Lambda對應的接口類型,才能使用Lambda作為該接口的實例。

有且僅有一個抽象方法的接口,稱為“函數式接口”。

Lambda標準格式

Lambda省去面向對象的條條框框,格式由3個部分組成:

  • 一些參數
  • 一個箭頭
  • 一段代碼

Lambda表達式的標準格式為:

(參數類型 參數名稱) ‐> { 代碼語句 }

格式說明:

  • 小括號內的語法與傳統方法參數列表一致:無參數則留空;多個參數則用逗號分隔。
  • -> 是新引入的語法格式,代表指向動作。
  • 大括號內的語法與傳統方法體要求基本一致。

線程匿名內部類的轉化(無參無返回值)

實現Runnable接口的匿名內部類的實現:

public class Demo01Runnable {
    public static void main(String[] args) {
        // 匿名內部類
        Runnable task = new Runnable() {
            @Override
            public void run() { // 覆蓋重寫抽象方法
                System.out.println("多線程任務執行!");
            }
        };
        new Thread(task).start(); // 啟動線程
    }
}

代碼分析

  • Thread 類需要Runnable 接口作為參數,其中的抽象run 方法是用來指定線程任務內容的核心;
  • 為了指定run 的方法體,不得不需要Runnable 接口的實現類;
  • 為了省去定義一個RunnableImpl 實現類的麻煩,不得不使用匿名內部類;
  • 必須覆蓋重寫抽象run 方法,所以方法名稱、方法參數、方法返回值不得不再寫一遍,且不能寫錯;
  • 而實際上,似乎只有方法體才是關鍵所在。

使用Lambda進行簡化:

public class Demo01Runnable {
    public static void main(String[] args) {
         new Thread(()->System.out.println("多線程任務執行!")).start(); // 啟動線程
    }
}

比較器內部類的轉化(有參有返回值)

使用比較器舉個例子:

// 創建數組
Integer[] arr = {1,8,3,4,9,2,5};

// 匿名內部類實現數組升序排序
Arrays.sort(arr,new Comparator<Integer>(){

    @Override
    public int compare(Integer o1, Integer o2) {
        return 0;
    }
});

// 使用lambda進行數組的升序排序
Arrays.sort(arr,( a, b)->{ return a - b ;});

Lambda表達式的省略規則

在Lambda標準格式的基礎上,使用省略寫法的規則為:

  1. 小括號內參數的類型可以省略;
  2. 如果小括號內有且僅有一個參,則小括號可以省略;
  3. 如果大括號內有且僅有一個語句,則無論是否有返回值,都可以省略大括號、return關鍵字及語句分號。

雖然有省略寫法,但是我感覺這個有點靈活,所以不建議省略,因為代碼是給別人看的,省略的話感覺別人看起來會有點費勁.

Java之線程池和Lambda表達式