執行器
執行器 文字說明
執行器 - 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.8。
2.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.分而治之的策略,讓我們的程序可以最大、最優、最快的運行,特別適合大數據的計算,和統計。