Java中併發工具包 - 下

執行器

執行器 文字說明

這裏寫圖片描述

執行器 - Callable與Futrue

這裏寫圖片描述

執行器 演示代碼

package com.performer.demo1;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 執行器 調用類
 * 演示目標
 *      兩條線程,分別計算 1 - 100 之間數字的和,100 - 100000 之間數字的和
 * */
public class DemoCase {
    public static void main(String[] args) throws Exception {
        //創建ExecutorService對象,且2個線程的執行器
        ExecutorService es = Executors.newFixedThreadPool(2); 

        /**
         * Future 是callabel返回值對象
         * es.submit()方法就是callabel提交到線程中執行
         * */
        Future<Integer> f1 = es.submit(new MC(1, 100)); // 提交任務
        Future<Integer> f2 = es.submit(new MC(100, 100000)); 

        //通過get()方法 獲取 call接口返回值
        System.out.println(f1.get() + ":" + f2.get());

        es.shutdown(); // 停止執行器
    }
}

// 1.實現 Callabel接口,並指定泛型的類型
class MC implements Callable<Integer> // Callable泛型 是指定執行結果的返回類型
{
    // 2.創建共有變量
    private int begin, end;

    // 創建類的有參構造函數
    public MC(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }

    /**
     * 3.實現了 callable接口方法 使用call方法計算 begin 到 end 數之間的和
     * call方法 返回線程執行結果
     * */
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = begin; i < end; i++) {
            sum += i;
        }
        return sum;
    }
}

執行器 - 運行結果

4950:704977754

執行器 使用總結

    執行器 API介紹 和使用步驟:
        1.在功能類中實現 Callable接口時,可以自定義線程的返回值類型。
        2.Callable接口 提供call方法,用於返回線程執行結果,
        3。在調用類中 首先創建 ExecutorService對象實例,使用ExecutorService 對象的 submit()方法 提交線程類。

        5.提交後,獲取到Future對象實例,通過它的 get()方法獲取線程執行的返回值。
        6.最後使用 ExecutorService線程管理對象的實例的shutdown() 方法,結束執行器。

ps:簡單來說
    Callable接口 的作用:功能類 實現 Callable接口 就是相當於實現了一個 有返回值的線程。

    ExecutorService接口 的作用:控制線程執行和管理線程

    Future接口 的作用:獲取 Callable接口實現類 的返回值

鎖與原子操作

鎖 與 原子操作 - API

這裏寫圖片描述

這裏寫圖片描述

鎖 與 原操作 - 演示代碼

package com.lock.demo1;

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

public class DemoCase {
    public static void main(String[] args) {
        new MT().start();
        new MT().start();
        new MT().start();
        new MT().start();
        new MT().start();
    }
}

class Data {
    static int i = 0;
    static Lock lock = new ReentrantLock(); // 創建鎖對象實例

    // static AtomicInteger ai = new AtomicInteger(0); //原子操作
    static void operate() {
        lock.lock(); // 開始加鎖,保護資源同一時間只能被一個線程操作

        i++;
        System.out.println(i);

        lock.unlock(); // 關閉鎖,因爲後面沒有資源需要保護
        // System.out.println(ai.incrementAndGet()); //原子操作 獲取方法
    }
}

class MT extends Thread {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Data.operate();
        }
    }
}

不加鎖 或者不使用原子操作的 運行結果

3
3
4
3
5
6
9
10
8
7
11
15
14
13
13

加鎖 或者使用原子操作後的結果

1
2
3
4
5
6
7
8
9
10
11
12
13

鎖 與 原子操作 使用總結

鎖 與 原子操作 的相同點:
    1.保護共有資源的獨立性,保證數據在同一時間只能被一條線程操作,從而保證數據的正確性和不重複性。

鎖 與 原子操作 的不相同點:
    1。使用鎖而獲取到數據是有序的,反之原子操作是無序的。 

流編程

流編程 - 基本知識

這裏寫圖片描述

流編程 - 流的基本操作

這裏寫圖片描述

<>

流編程 - 演示代碼

package com.stream;

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

public class DemoCase {
    public static void main(String[] args) {
        /**
         * 1.List:是一個有序的集合,可以包含重複的元素。提供了按索引訪問的方式。它繼承 Collection。
           2.List有兩個重要的實現類:ArrayList 和 LinkedList
           3.ArrayList:我們可以將其看作是能夠自動增長容量的數組。
           4.利用ArrayList的toArray()返回一個數組。
           5.Arrays.asList()返回一個列表。
         * */
        // 6.下面這樣寫法的好處:List是接口繼承於Collection接口。ArrayList是List接口的實現類。相當於一個動態數組
        List<String> list = new ArrayList<>();

        list.add("a");
        list.add("c");
        list.add("d");
        list.add("b");
        list.add("e");
        list.add("e");

        // ps:max是終端操作
//      //通過 stream方法獲取流 ,通過max方法求流中最大的值,String::compareTo是篩選器
//      Optional<String> max = list.stream().max(String::compareTo); 
//      
//      //使用get()方法回去返回值
//      System.out.println(max.get());

        //輸出流中不重複的數據的數量
        System.out.println("不重複的數據有:"+list.stream().distinct().count()+"個");

        //下面使用中間操作
        /**
         * 運行步驟是獲取流 -》進行排序,獲得到了中間流 -》使用foreach 循環遍歷 -》輸出
         * */
        System.out.println("數據排序後的輸出結果:");
        list.stream().sorted().forEach(e ->System.out.println(e));


    }
}

流編程 - 運行結果

不重複的數據有:5個
數據排序後的輸出結果:
a
b
c
d
e
e

使用總結

    1.首先 stream 是在JDK8纔有的,要使用必須保證自己的jdk版本是 1.82.eclispe的版本要在4.3以上,要支持1.8的語法。

    3.流的編程模型
        A.獲取流:stream/parallelSteam
        B.操作:sort/max/min...
    4.流編程的作用:簡化了我們處理數據,簡單直接的達到了我們想要的結果。 

Fork/Join 框架

Fork/Join - API

這裏寫圖片描述

分而治之策略

這裏寫圖片描述

Fork/Join - 演示代碼

package com.forkJoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

/**
 * 任務目標: 計算 1-10w的和
 * 實現步驟:分爲兩部分計算。分別計算1-5w的和,5w-10w的和
 * */
public class DemoCase {

    public static void main(String[] args) {
        /*
         * ForkJoinPool: 管理ForkJoinTask的線程池
         * */
        ForkJoinPool forkJoinPool = new ForkJoinPool(); 

        //Future 表示結果返回值
        Future<Long> result = forkJoinPool.submit(new MTask(0, 1000)); //提交

        try {
            System.out.println("總計:"+result.get()); //輸出
        } catch (InterruptedException | ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        forkJoinPool.shutdown(); // 停止
    }
}

/**
 * 功能類:1.繼承 RecursiveTask 抽象類,
 *      RecursiveTask 抽象類的特性:具有返回值
 * */
class MTask extends RecursiveTask<Long>
{

    //2.聲明共有變量和常量
    private int begin,end; //開始 和 結束變量
    /**
     * 根據 java API分而治之策略 建議臨界點定義在100-1000個操作中的位置
     * 
     * 限額1000,超過則繼續劃分成多個不超過1000的子任務
     * 
     * */
    static final int limit = 100; 
    //3.創建類的有參構造,傳入參數
    public MTask(int begin,int end)
    {
        this.begin = begin;
        this.end   = end  ;
    }
    /**
     *4. 實現 RecursiveTask抽象類的 compute方法,
     * compute方法的作用是,任務執行後獲取返回值
     * */
    @Override
    protected Long compute() {
        long sum = 0;
        //判斷是否超過限額1000
        if(end - begin <= limit)
        {
            for (int i = begin; i < end; i++) {
                sum += i;
            }
        }else //超過限額,把一個任務劃分2個子任務
        {
            int mid = (begin + end) / 2;  

            //拆分
            MTask left = new MTask(begin, mid);
            left.fork();

            MTask right= new MTask(mid + 1, end);
            right.fork();


            //分辨計算, 合併結果
            long lr = left.join();
            System.out.println(begin +"-"+mid+":"+lr);
            long rr = right.join();
            System.out.println(mid +"-"+end+":"+rr);

            sum = lr + rr ;
        }
        return sum;
    }
}

Fork/Join - 運行結果

251-313:17453
126-188:9703
0-62:1891
501-563:32953
63-125:5797
189-250:13359
314-375:20984
0-125:7688
564-625:36234
126-250:23062
251-375:38437
0-250:30750
376-438:25203
439-500:28609
751-813:48453
376-500:53812
501-625:69187
251-500:92249
814-875:51484
876-938:56203
0-500:122999
626-688:40703
939-1000:59109
751-875:99937
689-750:43859
876-1000:115312
626-750:84562
501-750:153749
751-1000:215249
501-1000:368998
總計:491997

Fork/Join - 使用總結

    Fork/Join 框架的特點的:
        1.分而治之的策略,讓我們的程序可以最大、最優、最快的運行,特別適合大數據的計算,和統計。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章