Java JUC總結(jié)
目錄
Java JUC簡介
內(nèi)存可見性 您炉、volatile關(guān)鍵字
CAS算法柒爵、原子變量
ConcurrentHashMap 鎖分段機制
CountDownLatch 閉鎖
Callable 接口
Lock 同步鎖、Condition 控制線程通信赚爵、線程按序交替
ReadWriteLock 讀寫鎖
線程八鎖
線程池
線程調(diào)度
ForkJoinPool 分支/合并框架 工作竊取
一棉胀、Java JUC簡介
在 Java 5.0 提供了 java.util.concurrent (簡稱JUC )包,在此包中增加了在并發(fā)編程中很常用的實用工具類冀膝,用于定義類似于線程的自定義子系統(tǒng)唁奢,包括線程池、異步 IO 和輕量級任務(wù)框架窝剖。提供可調(diào)的麻掸、靈活的線程池。還提供了設(shè)計用于多線程上下文中的 Collection 實現(xiàn)等赐纱。
二脊奋、內(nèi)存可見性 采郎、volatile關(guān)鍵字
-
內(nèi)存可見性
內(nèi)存可見性(Memory Visibility)是指當某個線程正在使用對象狀態(tài)而另一個線程在同時修改該狀態(tài),需要確保當一個線程修改了對象狀態(tài)后狂魔,其他線程能夠看到發(fā)生的狀態(tài)變化蒜埋。
可見性錯誤是指當讀操作與寫操作在不同的線程中執(zhí)行時,我們無法確保執(zhí)行讀操作的線程能適時地看到其他線程寫入的值最楷,有時甚至是根本不可能的事情整份。
我們可以通過同步來保證對象被安全地發(fā)布。除此之外我們也可以使用一種更加輕量級的 volatile變量籽孙。 volatile 關(guān)鍵字
Java 提供了一種稍弱的同步機制烈评,即 volatile 變量,用來確保將變量的更新操作通知到其他線程犯建〗补冢可以將 volatile 看做一個輕量級的鎖,但是又與鎖有些不同:
對于多線程适瓦,不是一種互斥關(guān)系
不能保證變量狀態(tài)的“原子性操作”
- 原子性操作解釋
例如 i++; 這個操作竿开,它不是一個原子性操作,在實際執(zhí)行時需要三步操作“讀-改-寫”:
int temp = i;
temp = temp + 1;
i = temp;
1
2
3
示例代碼
public class TestVolatile {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
while(true){
if(td.isFlag()){
System.out.println("------------------");
break;
}
}
}
}
class ThreadDemo implements Runnable {
private volatile boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
flag = true;
System.out.println("flag=" + isFlag());
}
public boolean isFlag() {
return flag;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
三玻熙、CAS算法否彩、原子變量
-
CAS算法
CAS (Compare-And-Swap) 是一種硬件對并發(fā)的支持,針對多處理器操作而設(shè)計的處理器中的一種特殊指令嗦随,用于管理對共享數(shù)據(jù)的并發(fā)訪問列荔。
CAS 是一種無鎖的非阻塞算法的實現(xiàn)。
CAS 包含了 3 個操作數(shù):需要讀寫的內(nèi)存值 V枚尼、進行比較的值 A贴浙、擬寫入的新值 B
當且僅當 V 的值等于 A 時,CAS 通過原子方式用新值 B 來更新 V 的值署恍,否則不會執(zhí)行任何操作崎溃。
模擬CAS算法
public class TestCompareAndSwap {
public static void main(String[] args) {
final CompareAndSwap cas = new CompareAndSwap();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int expectedValue = cas.get();
boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
System.out.println(b);
}
}).start();
}
}
}
class CompareAndSwap{
private int value;
//獲取內(nèi)存值
public synchronized int get(){
return value;
}
//比較
public synchronized int compareAndSwap(int expectedValue, int newValue){
int oldValue = value;
if(oldValue == expectedValue){
this.value = newValue;
}
return oldValue;
}
//設(shè)置
public synchronized boolean compareAndSet(int expectedValue, int newValue){
return expectedValue == compareAndSwap(expectedValue, newValue);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
-
原子變量
類的小工具包,支持在單個變量上解除鎖的線程安全編程锭汛。事實上笨奠,此包中的類可將 volatile 值、字段和數(shù)組元素的概念擴展到那些也提供原子條件更新操作的唤殴。
類 AtomicBoolean般婆、AtomicInteger、AtomicLong 和 AtomicReference 的實例各自提供對相應(yīng)類型單個變量的訪問和更新朵逝。每個類也為該類型提供適當?shù)膶嵱霉ぞ叻椒ā?br> AtomicIntegerArray蔚袍、AtomicLongArray 和 AtomicReferenceArray 類進一步擴展了原子操作,對這些類型的數(shù)組提供了支持。這些類在為其數(shù)組元素提供 volatile 訪問語義方面也引人注目啤咽,這對于普通數(shù)組來說是不受支持的晋辆。
核心方法:boolean compareAndSet(expectedValue, updateValue)
java.util.concurrent.atomic 包下提供了一些原子操作的常用類:AtomicBoolean 、AtomicInteger 宇整、AtomicLong 瓶佳、 AtomicReference
AtomicIntegerArray 、AtomicLongArray
AtomicMarkableReference
AtomicReferenceArray
AtomicStampedReference
示例代碼
import java.util.concurrent.atomic.AtomicInteger;
public class TestAtomicDemo {
public static void main(String[] args) {
AtomicDemo ad = new AtomicDemo();
for (int i = 0; i < 10; i++) {
new Thread(ad).start();
}
}
}
class AtomicDemo implements Runnable{
// private volatile int serialNumber = 0;
private AtomicInteger serialNumber = new AtomicInteger(0);
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
System.out.println(getSerialNumber());
}
public int getSerialNumber(){
return serialNumber.getAndIncrement(); // 原子執(zhí)行自增1操作
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
四鳞青、ConcurrentHashMap 鎖分段機制
Java 5.0 在 java.util.concurrent 包中提供了多種并發(fā)容器類來改進同步容器的性能霸饲。
ConcurrentHashMap 同步容器類是Java 5 增加的一個線程安全的哈希表。對與多線程的操作臂拓,介于 HashMap 與 Hashtable 之間厚脉。內(nèi)部采用“鎖分段”機制替代 Hashtable 的獨占鎖。進而提高性能胶惰。
此包還提供了設(shè)計用于多線程上下文中的 Collection 實現(xiàn):ConcurrentHashMap傻工、ConcurrentSkipListMap、ConcurrentSkipListSet孵滞、CopyOnWriteArrayList 和CopyOnWriteArraySet中捆。當期望許多線程訪問一個給定 collection 時,ConcurrentHashMap 通常優(yōu)于同步的 HashMap剃斧,ConcurrentSkipListMap 通常優(yōu)于同步的 TreeMap轨香。當期望的讀數(shù)和遍歷遠遠大于列表的更新數(shù)時忽你,CopyOnWriteArrayList 優(yōu)于同步的 ArrayList幼东。
示例代碼
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
/*
CopyOnWriteArrayList/CopyOnWriteArraySet : “寫入并復(fù)制”
-
注意:添加操作多時,效率低科雳,因為每次添加時都會進行復(fù)制根蟹,開銷非常的大。并發(fā)迭代操作多時可以選擇糟秘。
*/
public class TestCopyOnWriteArrayList {public static void main(String[] args) {
HelloThread ht = new HelloThread();for (int i = 0; i < 10; i++) { new Thread(ht).start(); }
}
}
class HelloThread implements Runnable{
// private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
static{
list.add("AA");
list.add("BB");
list.add("CC");
}
@Override
public void run() {
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
list.add("AA");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
五简逮、CountDownLatch 閉鎖
Java 5.0 在 java.util.concurrent 包中提供了多種并發(fā)容器類來改進同步容器的性能。
CountDownLatch 是一個同步輔助類尿赚,在完成一組正在其他線程中執(zhí)行的操作之前散庶,它允許一個或多個線程一直等待。
閉鎖可以延遲線程的進度直到其到達終止狀態(tài)凌净,閉鎖可以用來確保某些活動直到其他活動都完成才繼續(xù)執(zhí)行:
確保某個計算在其需要的所有資源都被初始化之后才繼續(xù)執(zhí)行;
確保某個服務(wù)在其依賴的所有其他服務(wù)都已經(jīng)啟動之后才啟動;
等待直到某個操作所有參與者都準備就緒再繼續(xù)執(zhí)行悲龟。
示例程序
import java.util.concurrent.CountDownLatch;
/**
-
CountDownLatch:閉鎖,在完成某些運算時冰寻,只有其他所有線程的運算全部完成须教,當前運算才繼續(xù)執(zhí)行
*/
public class TestCountDownLatch {public static void main(String[] agrs) {
CountDownLatch latch = new CountDownLatch(5); // 5表示有5個線程
LatchDemo ld = new LatchDemo(latch);long start = System.currentTimeMillis(); for (int i = 0; i < 5; i++) { new Thread(ld).start(); } try { latch.await(); // 等待 } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("耗費時間為:" + (end - start) + "ms");
}
}
class LatchDemo implements Runnable {
private CountDownLatch latch;
public LatchDemo(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
try {
for (int i = 0; i < 50000; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
} finally { // 必須執(zhí)行的操作
latch.countDown();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
六、Callable 接口
Java 5.0 在 java.util.concurrent 提供了一個新的創(chuàng)建執(zhí)行線程的方式:Callable 接口
Callable 接口類似于 Runnable,兩者都是為那些其實例可能被另一個線程執(zhí)行的類設(shè)計的轻腺。但是 Runnable 不會返回結(jié)果乐疆,并且無法拋出經(jīng)過檢查的異常。
Callable 需要依賴FutureTask 贬养,F(xiàn)utureTask 也可以用作閉鎖挤土。
示例程序
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
一、創(chuàng)建執(zhí)行線程的方式三:實現(xiàn)Callable接口误算。相較于實現(xiàn)Runnable接口的方式耕挨,方法可以有返回值,并且可以拋出異常
-
二尉桩、執(zhí)行Callable方式筒占,需要FutureTask實現(xiàn)類的支持,用于接受運算結(jié)果蜘犁。FutureTask是Future接口的實現(xiàn)類
*/
public class TestCallable {public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();// 1.執(zhí)行Callable方式翰苫,需要FutureTask實現(xiàn)類的支持,用于接受運算結(jié)果 FutureTask<Integer> result = new FutureTask<>(td); new Thread(result).start(); // 2.接收線程運算后的結(jié)果 try { Integer sum = result.get(); // FutureTask可用于閉鎖 System.out.println(sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
}
}
class ThreadDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
七这橙、Lock 同步鎖奏窑、Condition 控制線程通信、線程按序交替
-
顯示鎖 Lock
在 Java 5.0 之前屈扎,協(xié)調(diào)共享對象的訪問時可以使用的機制只有 synchronized 和 volatile 埃唯。Java 5.0 后增加了一些新的機制,但并不是一種替代內(nèi)置鎖的方法鹰晨,而是當內(nèi)置鎖不適用時墨叛,作為一種可選擇的高級功能。
ReentrantLock 實現(xiàn)了 Lock 接口模蜡,并提供了與synchronized 相同的互斥性和內(nèi)存可見性漠趁。但相較于synchronized 提供了更高的處理鎖的靈活性。
示例代碼
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
一忍疾、用于解決多線程安全問題的方式:
synchronized:隱式鎖
- 同步代碼塊
- 同步方法
jdk 1.5 后:
- 同步鎖 Lock
-
注意:是一個顯示鎖闯传,需要通過 lock() 方法上鎖,必須通過 unlock() 方法進行釋放鎖
*/
public class TestLock {public static void main(String[] args) {
Ticket ticket = new Ticket();new Thread(ticket, "1號窗口").start(); new Thread(ticket, "2號窗口").start(); new Thread(ticket, "3號窗口").start();
}
}
class Ticket implements Runnable{
private int tick = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while(true){
lock.lock(); //上鎖
try{
if(tick > 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + " 完成售票卤妒,余票為:" + --tick);
}
}finally{
lock.unlock(); //釋放鎖
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
-
Condition 控制線程通信
Condition 接口描述了可能會與鎖有關(guān)聯(lián)的條件變量甥绿。這些變量在用法上與使用 Object.wait 訪問的隱式監(jiān)視器類似,但提供了更強大的功能则披。需要特別指出的是共缕,單個 Lock 可能與多個 Condition 對象關(guān)聯(lián)。為了避免兼容性問題收叶,Condition 方法的名稱與對應(yīng)的 Object 版本中的不同骄呼。
在 Condition 對象中,與 wait、notify 和 notifyAll 方法對應(yīng)的分別是await蜓萄、signal 和 signalAll隅茎。
Condition 實例實質(zhì)上被綁定到一個鎖上。要為特定 Lock 實例獲得Condition 實例嫉沽,請使用其 newCondition() 方法辟犀。 線程按序交替
Lock和Condition結(jié)合應(yīng)用以實現(xiàn)線程按序交替。
案例:
編寫一個程序绸硕,開啟 3 個線程堂竟,這三個線程的 ID 分別為A洒疚、B宾添、C秉版,每個線程將自己的 ID 在屏幕上打印 10 遍拼卵,要求輸出的結(jié)果必須按順序顯示。如:ABCABCABC…… 依次遞歸问欠。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestABCAlternate {
public static void main(String[] agrs) {
AlternateDemo ad = new AlternateDemo();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
ad.loopA();
}
}
}, "A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
ad.loopB();
}
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
ad.loopC();
}
}
}, "C").start();
}
}
class AlternateDemo {
private int number = 1; // 當前正在執(zhí)行的線程標記
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void loopA() {
lock.lock();
try {
// 1.判斷
if (number != 1) {
condition1.await();
}
// 2.打印
System.out.print(Thread.currentThread().getName());
// 3.喚醒
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void loopB() {
lock.lock();
try {
if (number != 2) {
try {
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
number = 3;
condition3.signal();
} finally {
lock.unlock();
}
}
public void loopC() {
lock.lock();
try {
if (number != 3) {
try {
condition3.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
number = 1;
condition1.signal();
} finally {
lock.unlock();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
八身害、ReadWriteLock 讀寫鎖
ReadWriteLock 維護了一對相關(guān)的鎖狞贱,一個用于只讀操作垮斯,另一個用于寫入操作郎仆。只要沒有 writer,讀取鎖可以由多個 reader 線程同時保持兜蠕。寫入鎖是獨占的扰肌。
ReadWriteLock 讀取操作通常不會改變共享資源,但執(zhí)行寫入操作時熊杨,必須獨占方式來獲取鎖曙旭。對于讀取操作占多數(shù)的數(shù)據(jù)結(jié)構(gòu)。 ReadWriteLock 能提供比獨占鎖更高的并發(fā)性猴凹。而對于只讀的數(shù)據(jù)結(jié)構(gòu)夷狰,其中包含的不變性可以完全不需要考慮加鎖操作。
示例代碼
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
- ReadWriteLock : 讀寫鎖
- 寫寫/讀寫 需要“互斥”
- 讀讀 不需要互斥
*/
public class TestReadWriteLock {
public static void main(String[] args) {
ReadWriteLockDemo rw = new ReadWriteLockDemo();
new Thread(new Runnable() {
@Override
public void run() {
rw.set((int)(Math.random() * 101));
}
}, "Write:").start();
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
rw.get();
}
}).start();
}
}
}
class ReadWriteLockDemo{
private int number = 0;
private ReadWriteLock lock = new ReentrantReadWriteLock();
//讀
public void get(){
lock.readLock().lock(); //上鎖
try{
System.out.println(Thread.currentThread().getName() + " : " + number);
}finally{
lock.readLock().unlock(); //釋放鎖
}
}
//寫
public void set(int number){
lock.writeLock().lock();
try{
System.out.println(Thread.currentThread().getName());
this.number = number;
}finally{
lock.writeLock().unlock();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
九郊霎、線程八鎖
一個對象里面如果有多個synchronized方法,某一個時刻內(nèi)爷绘,只要一個線程去調(diào)用其中的一個synchronized方法了书劝,其它的線程都只能等待,換句話說土至,某一個時刻內(nèi)购对,只能有唯一一個線程去訪問這些synchronized方法。
鎖的是當前對象this陶因,被鎖定后骡苞,其它的線程都不能進入到當前對象的其它的synchronized方法。
加個普通方法后發(fā)現(xiàn)和同步鎖無關(guān)。
換成兩個對象后解幽,不是同一把鎖了贴见,情況立刻變化。
都換成靜態(tài)同步方法后躲株,情況又變化片部。
總結(jié):
所有的非靜態(tài)同步方法用的都是同一把鎖——實例對象本身,也就是說如果一個實例對象的非靜態(tài)同步方法獲取鎖后霜定,該實例對象的其他非靜態(tài)同步方法必須等待獲取鎖的方法釋放鎖后才能獲取鎖档悠,可是別的實例對象的非靜態(tài)同步方法因為跟該實例對象的非靜態(tài)同步方法用的是不同的鎖,所以毋須等待該實例對象已獲取鎖的非靜態(tài)同步方法釋放鎖就可以獲取他們自己的鎖望浩。
所有的靜態(tài)同步方法用的也是同一把鎖——類對象本身辖所,這兩把鎖是兩個不同的對象,所以靜態(tài)同步方法與非靜態(tài)同步方法之間是不會有競態(tài)條件的磨德。但是一旦一個靜態(tài)同步方法獲取鎖后奴烙,其他的靜態(tài)同步方法都必須等待該方法釋放鎖后才能獲取鎖,而不管是同一個實例對象的靜態(tài)同步方法之間剖张,還是不同的實例對象的靜態(tài)同步方法之間切诀,只要它們同一個類的實例對象!
十搔弄、線程池
第四種獲取線程的方法:線程池幅虑,一個 ExecutorService,它使用可能的幾個池線程之一執(zhí)行每個提交的任務(wù)顾犹,通常使用 Executors 工廠方法配置倒庵。
線程池可以解決兩個不同問題:由于減少了每個任務(wù)調(diào)用的開銷,它們通踌潘ⅲ可以在執(zhí)行大量異步任務(wù)時提供增強的性能擎宝,并且還可以提供綁定和管理資源(包括執(zhí)行任務(wù)集時使用的線程)的方法。每個 ThreadPoolExecutor 還維護著一些基本的統(tǒng)計數(shù)據(jù)浑玛,如完成的任務(wù)數(shù)绍申。
為了便于跨大量上下文使用,此類提供了很多可調(diào)整的參數(shù)和擴展鉤子 (hook)顾彰。但是极阅,強烈建議程序員使用較為方便的 Executors 工廠方法 :
Executors.newCachedThreadPool()(無界線程池,可以進行自動線程回收)
Executors.newFixedThreadPool(int)(固定大小線程池)
Executors.newSingleThreadExecutor()(單個后臺線程)
它們均為大多數(shù)使用場景預(yù)定義了設(shè)置涨享。
示例代碼
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*
一筋搏、線程池:提供了一個線程隊列,隊列中保存著所有等待狀態(tài)的線程厕隧。避免了創(chuàng)建與銷毀額外開銷奔脐,提高了響應(yīng)的速度俄周。
二、線程池的體系結(jié)構(gòu):
java.util.concurrent.Executor : 負責(zé)線程的使用與調(diào)度的根接口
|--**ExecutorService 子接口: 線程池的主要接口
|--ThreadPoolExecutor 線程池的實現(xiàn)類
|--ScheduledExecutorService 子接口:負責(zé)線程的調(diào)度
|--ScheduledThreadPoolExecutor :繼承 ThreadPoolExecutor髓迎, 實現(xiàn) ScheduledExecutorService
三峦朗、工具類 : Executors
ExecutorService newFixedThreadPool() : 創(chuàng)建固定大小的線程池
ExecutorService newCachedThreadPool() : 緩存線程池,線程池的數(shù)量不固定竖般,可以根據(jù)需求自動的更改數(shù)量甚垦。
ExecutorService newSingleThreadExecutor() : 創(chuàng)建單個線程池。線程池中只有一個線程
-
ScheduledExecutorService newScheduledThreadPool() : 創(chuàng)建固定大小的線程涣雕,可以延遲或定時的執(zhí)行任務(wù)艰亮。
*/
public class TestThreadPool {public static void main(String[] args) throws Exception {
//1. 創(chuàng)建線程池
ExecutorService pool = Executors.newFixedThreadPool(5);List<Future<Integer>> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { Future<Integer> future = pool.submit(new Callable<Integer>(){ @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i <= 100; i++) { sum += i; } return sum; } }); list.add(future); } pool.shutdown(); for (Future<Integer> future : list) { System.out.println(future.get()); } /*ThreadPoolDemo tpd = new ThreadPoolDemo(); //2. 為線程池中的線程分配任務(wù) for (int i = 0; i < 10; i++) { pool.submit(tpd); } //3. 關(guān)閉線程池 pool.shutdown();*/
}
// new Thread(tpd).start();
// new Thread(tpd).start();
}
class ThreadPoolDemo implements Runnable{
private int i = 0;
@Override
public void run() {
while(i <= 100){
System.out.println(Thread.currentThread().getName() + " : " + i++);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
十一、線程調(diào)度
- ScheduledExecutorService
一個 ExecutorService挣郭,可安排在給定的延遲后運行或定期執(zhí)行的命令迄埃。
示例代碼
public class TestScheduledThreadPool {
public static void main(String[] args) throws Exception {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 5; i++) {
Future<Integer> result = pool.schedule(new Callable<Integer>(){
@Override
public Integer call() throws Exception {
int num = new Random().nextInt(100);//生成隨機數(shù)
System.out.println(Thread.currentThread().getName() + " : " + num);
return num;
}
}, 1, TimeUnit.SECONDS);
System.out.println(result.get());
}
pool.shutdown();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
十二、ForkJoinPool 分支/合并框架 工作竊取
- Fork/Join 框架
Fork/Join 框架:就是在必要的情況下兑障,將一個大任務(wù)侄非,進行拆分(fork)成若干個小任務(wù)(拆到不可再拆時),再將一個個的小任務(wù)運算的結(jié)果進行 join 匯總流译。在這里插入圖片描述
-
Fork/Join 框架與線程池的區(qū)別
采用 “工作竊取”模式(work-stealing):
當執(zhí)行新的任務(wù)時它可以將其拆分分成更小的任務(wù)執(zhí)行逞怨,并將小任務(wù)加到線程隊列中,然后再從一個隨機線程的隊列中偷一個并把它放在自己的隊列中福澡。
相對于一般的線程池實現(xiàn)叠赦,fork/join框架的優(yōu)勢體現(xiàn)在對其中包含的任務(wù)的處理方式上.在一般的線程池中,如果一個線程正在執(zhí)行的任務(wù)由于某些原因無法繼續(xù)運行革砸,那么該線程會處于等待狀態(tài)除秀。而在fork/join框架實現(xiàn)中,如果某個子問題由于等待另外一個子問題的完成而無法繼續(xù)運行算利。那么處理該子問題的線程會主動尋找其他尚未運行的子問題來執(zhí)行.這種方式減少了線程的等待時間册踩,提高了性能。
示例代碼
public class TestForkJoinPool {
public static void main(String[] args) {
Instant start = Instant.now();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L, 50000000000L);
Long sum = pool.invoke(task);
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗費時間為:" + Duration.between(start, end).toMillis());//166-1996-10590
}
//普通串行計算
@Test
public void test1(){
Instant start = Instant.now();
long sum = 0L;
for (long i = 0L; i <= 50000000000L; i++) {
sum += i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗費時間為:" + Duration.between(start, end).toMillis());//35-3142-15704
}
//java8 新特性
@Test
public void test2(){
Instant start = Instant.now();
Long sum = LongStream.rangeClosed(0L, 50000000000L)
.parallel()
.reduce(0L, Long::sum);
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗費時間為:" + Duration.between(start, end).toMillis());//1536-8118
}
}
class ForkJoinSumCalculate extends RecursiveTask<Long>{
/**
*
*/
private static final long serialVersionUID = -259195479995561737L;
private long start;
private long end;
private static final long THURSHOLD = 10000L; //臨界值
public ForkJoinSumCalculate(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end - start;
if(length <= THURSHOLD){
long sum = 0L;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else{
long middle = (start + end) / 2;
ForkJoinSumCalculate left = new ForkJoinSumCalculate(start, middle);
left.fork(); //進行拆分效拭,同時壓入線程隊列
ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle+1, end);
right.fork(); //
return left.join() + right.join();
}
}
}
————————————————
版權(quán)聲明:本文為CSDN博主「頻率coo」的原創(chuàng)文章暂吉,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明允耿。
原文鏈接:https://blog.csdn.net/qq_40121502/article/details/88219548