Java JUC 併發程式設計
Java JUC簡介
在Java5.0提供了java.util.concurrent包,簡稱JUC包,在此包中增加了在併發程式設計中很常用的實用工具類,用於定義類似於縣城的自定義子系統,包括執行緒池、非同步 IO 和輕量級任務框架。提供可調的、靈活的執行緒池。還提供了設計用於多執行緒上下文中的Collection實現等。
1.-記憶體可見性
記憶體可見性(Memory Visibility)是指當某個執行緒正在使用物件狀態而另一個執行緒在同時修改該狀態,需要確保當一個執行緒修改了物件狀態後,其他執行緒能夠看到發生的狀態變化。
可見性錯誤是指當讀操作與寫操作在不同的執行緒中執行時,我們無法確保執行讀操作的執行緒能適時地看到其他執行緒寫入的值,有時甚至是根本不可能的事情。
我們可以通過同步來保證物件被安全地釋出。除此之外我們也可以使用一種更加輕量級的 volatile 變數。
-volatile關鍵字
Java 提供了一種稍弱的同步機制,即 volatile 變數,用來確保將變數的更新操作通知到其他執行緒。可以將 volatile 看做一個輕量級的鎖,但是又與鎖有些不同:
①.對於多執行緒,不是一種互斥關係
②.不能保證變數狀態的"原子性操作"
package com.atguigu.juc; /* * 一、volatile 關鍵字:當多個執行緒進行操作共享資料時,可以保證記憶體中的資料可見。 * 相較於 synchronized 是一種較為輕量級的同步策略。 * * 注意: * 1. volatile 不具備“互斥性” * 2. volatile 不能保證變數的“原子性” */ 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; } public void setFlag(boolean flag) { this.flag = flag; } }
2-原子變數
類的小工具包,支援在單個變數上解除鎖的執行緒安全程式設計。事實上,此包中的類可將 volatile 值、欄位和陣列元素的概念擴充套件到那些也提供原子條件更新操作的類
類 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的例項各自提供對相應型別單個變數的訪問和更新。每個類也為該型別提供適當的實用工具方法
AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 類進一步擴充套件了原子操作,對這些型別的陣列提供了支援。這些類在為其陣列元素提供 volatile 訪問語義方面也引人注目,這對於普通陣列來說是不受支援的。
核心方法:boolean compareAndSet(expectedValue, updateValue)
CAS演算法
CAS (Compare-And-Swap) 是一種硬體對併發的支援,針對多處理器操作而設計的處理器中的一種特殊指令,用於管理對共享資料的併發訪問。
CAS 是一種無鎖的非阻塞演算法的實現。
CAS包含了3個運算元 :需要讀寫的記憶體值V 進行比較的值A 擬寫入的新值B
當且僅當V的值等於A時,CAS通過原子方式用新值B來更新V的值,否則不執行操作。
例如執行緒一對主存中的共享資料sn進行++操作,++操作底層的實現應該是:int temp = i; i = i + 1; i= temp; 這三步,當讀寫的記憶體值V=0時,判斷記憶體值V是否等於預估值A,如果等於,則將新值B賦值給A,否則可得知此變數因為別的執行緒操作完成已經發生了改變,不進行操作。
CAS演算法保證了資料的原子性!!!!
package com.atguigu.juc;
import java.util.concurrent.atomic.AtomicInteger;
/*
* 一、i++ 的原子性問題:i++ 的操作實際上分為三個步驟“讀-改-寫”
* int i = 10;
* i = i++; //10
*
* int temp = i;
* i = i + 1;
* i = temp;
*
* 二、原子變數:在 java.util.concurrent.atomic 包下提供了一些原子變數。
* 1. volatile 保證記憶體可見性
* 2. CAS(Compare-And-Swap) 演算法保證資料變數的原子性
* CAS 演算法是硬體對於併發操作的支援
* CAS 包含了三個運算元:
* ①記憶體值 V
* ②預估值 A
* ③更新值 B
* 當且僅當 V == A 時, V = B; 否則,不會執行任何操作。
*/
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();//i++操作
}
}
3- ConcurrentHashMap 鎖分段機制
是一個執行緒安全的hash表。
之前的HashMap是執行緒不安全的,HashTable是執行緒安全的,HashTable的鎖是鎖整個表,當多個執行緒同時訪問HashTable這個表時,並行改為了序列,只能允許一個執行緒同時訪問這個HashTable表,導致效率非常低。HashTable也會有複合操作安全性問題。
ConcurrentHashMap:採用鎖分段機制,ConcurrentLevel為分段級別,預設的級別為16,每個段中有個hash表,有獨立的鎖,支援多個執行緒同時訪問Map。
Java8過後又對其進行了升級,將鎖分段機制取消,以CAS演算法來代替鎖,不會出現執行緒阻塞,效率更高!
CopyOnWriteArrayList
解決了併發修改異常,如果使用Collections類中的synchronizedList(new ArrayList<String>()),當在往此容器中一邊新增一邊拿迭代器遍歷的時候就會出現併發修改異常,所以在遍歷讀取操作大於更新數的時候,CopyOnWriteArrayList優於同步的ArrayList。
package com.atguigu.juc;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
/*
* CopyOnWriteArrayList/CopyOnWriteArraySet : “寫入並複製”
* 注意:新增操作多時,效率低,因為每次新增時都會進行復制,開銷非常的大。併發迭代操作多時可以選擇。
*/
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");
}
}
}
4-CountDownLatch 閉鎖
CountDownLatch是一個同步輔助類,在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待。
package com.atguigu.juc;
import java.util.concurrent.CountDownLatch;
/*
* CountDownLatch :閉鎖,在完成某些運算是,只有其他所有執行緒的運算全部完成,當前運算才繼續執行
*/
public class TestCountDownLatch {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(50);
LatchDemo ld = new LatchDemo(latch);
long start = System.currentTimeMillis();
for (int i = 0; i < 50; i++) {
new Thread(ld).start();
}
try {
latch.await();
} catch (InterruptedException e) {
}
long end = System.currentTimeMillis();
System.out.println("耗費時間為:" + (end - start));
}
}
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 {
latch.countDown();
}
}
}
5.通過實現Callable介面建立並執行多執行緒
通過實現Callable介面,並實現裡面的call()的方法進行建立多執行緒。相較於實現Runnable介面的方式,此方法可以有返回值,並且可以丟擲異常。
通過這種方式建立了多執行緒,在需要執行的時候不能直接將類的物件傳給Thread通過start方法呼叫,而需要一個FutureTask這個實現類物件,需要這個類的物件接收執行緒結束後的返回值。
package com.atguigu.juc;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/*
* 一、建立執行執行緒的方式三:實現 Callable 介面。 相較於實現 Runnable 介面的方式,方法可以有返回值,並且可以丟擲異常。
*
* 二、執行 Callable 方式,需要 FutureTask 實現類的支援,用於接收運算結果。 FutureTask 是 Future 介面的實現類
*/
public class TestCallable {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
//1.執行 Callable 方式,需要 FutureTask 實現類的支援,用於接收運算結果。
FutureTask<Integer> result = new FutureTask<>(td);
new Thread(result).start();
//2.接收執行緒運算後的結果
try {
Integer sum = result.get(); //FutureTask 可用於 閉鎖
System.out.println(sum);
System.out.println("------------------------------------");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
class ThreadDemo implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100000; i++) {
sum += i;
}
return sum;
}
}
/*class ThreadDemo implements Runnable{
@Override
public void run() {
}
}*/
6.同步鎖 Lock
package com.atguigu.juc;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 一、用於解決多執行緒安全問題的方式:
*
* synchronized:隱式鎖
* 1. 同步程式碼塊
*
* 2. 同步方法
*
* jdk 1.5 後:
* 3. 同步鎖 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(); //釋放鎖
}
}
}
}
7-生產者與消費者模式以及用Lock鎖來代替Sychronized鎖
生產者與消費者模式:
/*
* 生產者和消費者案例
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer cus = new Consumer(clerk);
new Thread(pro, "生產者 A").start();
new Thread(cus, "消費者 B").start();
new Thread(pro, "生產者 C").start();
new Thread(cus, "消費者 D").start();
}
}
//店員
class Clerk{
private int product = 0;
//進貨
public synchronized void get(){//迴圈次數:0
while(product >= 1){//為了避免虛假喚醒問題,應該總是使用在迴圈中
System.out.println("產品已滿!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + ++product);
this.notifyAll();
}
//賣貨
public synchronized void sale(){//product = 0; 迴圈次數:0
while(product <= 0){
System.out.println("缺貨!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + --product);
this.notifyAll();
}
}
//生產者
class Productor implements Runnable{
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
clerk.get();
}
}
}
//消費者
class Consumer implements Runnable{
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}
如用Lock鎖解決:則應該把this的wait與notifyAll方法換為Condition介面的await與signalALL方法
this的notify方法換為signal方法
package com.atguigu.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 生產者消費者案例:
*/
public class TestProductorAndConsumerForLock {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer con = new Consumer(clerk);
new Thread(pro, "生產者 A").start();
new Thread(con, "消費者 B").start();
// new Thread(pro, "生產者 C").start();
// new Thread(con, "消費者 D").start();
}
}
class Clerk {
private int product = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
// 進貨
public void get() {
lock.lock();
try {
if (product >= 1) { // 為了避免虛假喚醒,應該總是使用在迴圈中。
System.out.println("產品已滿!");
try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ ++product);
condition.signalAll();
} finally {
lock.unlock();
}
}
// 賣貨
public void sale() {
lock.lock();
try {
if (product <= 0) {
System.out.println("缺貨!");
try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ --product);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
// 生產者
class Productor implements Runnable {
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
// 消費者
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}
8.執行緒按需交替
編寫一個程式,開啟 3 個執行緒,這三個執行緒的 ID 分別為A、B、C,每個執行緒將自己的 ID 在螢幕上列印 10 遍,要求輸出的結果必須按順序顯示。如:ABCABCABC…… 依次遞迴
需要用Lock鎖、Condition介面實現類物件控制各個執行緒的通訊。
package com.atguigu.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 編寫一個程式,開啟 3 個執行緒,這三個執行緒的 ID 分別為 A、B、C,每個執行緒將自己的 ID 在螢幕上列印 10 遍,要求輸出的結果必須按順序顯示。
* 如:ABCABCABC…… 依次遞迴
*/
public class TestABCAlternate {
public static void main(String[] args) {
AlternateDemo ad = new AlternateDemo();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
ad.loopA(i);
}
}
}, "A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
ad.loopB(i);
}
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
ad.loopC(i);
System.out.println("-----------------------------------");
}
}
}, "C").start();
}
}
class AlternateDemo{
private int number = 1; //當前正在執行執行緒的標記
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
/**
* @param totalLoop : 迴圈第幾輪
*/
public void loopA(int totalLoop){
lock.lock();
try {
//1. 判斷
if(number != 1){
condition1.await();
}
//2. 列印
for (int i = 1; i <= 1; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
//3. 喚醒
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void loopB(int totalLoop){
lock.lock();
try {
//1. 判斷
if(number != 2){
condition2.await();
}
//2. 列印
for (int i = 1; i <= 1; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
//3. 喚醒
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void loopC(int totalLoop){
lock.lock();
try {
//1. 判斷
if(number != 3){
condition3.await();
}
//2. 列印
for (int i = 1; i <= 1; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
//3. 喚醒
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
9.讀-寫鎖 ReadWriteLock
ReadWriteLock 維護了一對相關的鎖,一個用於只讀操作,另一個用於寫入操作。只要沒有 writer,讀取鎖可以由多個 reader 執行緒同時保持。寫入鎖是獨佔的。
ReadWriteLock 讀取操作通常不會改變共享資源,但執行寫入操作時,必須獨佔方式來獲取鎖。對於讀取操作佔多數的資料結構。 ReadWriteLock 能提供比獨佔鎖更高的併發性。而對於只讀的資料結構,其中包含的不變性可以完全不需要考慮加鎖操作
package com.atguigu.juc;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
* 1. 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();
}
}
}
10.-執行緒八鎖
題目:判斷列印的"one" or "two"??
①兩個普通的方法,兩個執行緒,列印? // one two
②新增Thread.sleep()給getOne(),列印? // one two
③新增普通方法getThree(),列印? //three one two
④兩個普通同步方法,兩個Number物件,列印? // two one
⑤修改getOne()為靜態方法,一個Number物件,列印? // two one
⑥修改兩個方法均均為靜態方法,一個Number物件? // one two
⑦一個靜態同步方法,一個非靜態同步方法,兩個Number物件? // two one
⑧兩個靜態同步方法,兩個Number物件? //one two
總結:
①一個物件裡面如果有多個synchronized方法,某一個時刻內,只要一個執行緒去呼叫其中的一個synchronized方法了,其他的執行緒都只能等待,換句話說,某一時刻內,只能有唯一一個執行緒去訪問這些synchronized方法。
②鎖的是當前物件this,被鎖定後,其他執行緒都不能進入到當前物件的其他的synchronized方法。
③加個普通方法後發現和同步鎖無關。
④換成靜態同步方法後,情況又變化
⑤所有的非靜態同步方法用的都是同一把鎖 -- 例項物件本身,也就是說如果一個例項物件的非靜態同步方法獲取鎖後,該例項物件的其他非靜態同步方法必須等待獲取鎖的方法釋放鎖後才能獲取鎖,可是別的例項物件的非靜態同步方法因為跟該例項物件的非靜態同步方法用的是不同的鎖,所以毋須等待該例項物件已經取鎖的非靜態同步方法釋放鎖就可以獲取他們自己的鎖。
⑥所有的靜態同步方法用的也是同一把鎖 -- 類物件本身,這兩把鎖是兩個不同的物件,所以靜態同步方法與非靜態同步方法之間不會有競爭條件。但是一旦一個靜態同步方法獲取鎖後,其他的靜態同步方法都必須等待該方法釋放鎖後才能獲取鎖,而不管是同一個例項物件的靜態同步方法之間,還是不同的例項物件的靜態同步方法之間,只要它們是同一個例項物件!
package com.atguigu.juc;
/*
* 題目:判斷列印的 "one" or "two" ?
*
* 1. 兩個普通同步方法,兩個執行緒,標準列印, 列印? //one two
* 2. 新增 Thread.sleep() 給 getOne() ,列印? //one two
* 3. 新增普通方法 getThree() , 列印? //three one two
* 4. 兩個普通同步方法,兩個 Number 物件,列印? //two one
* 5. 修改 getOne() 為靜態同步方法,列印? //two one
* 6. 修改兩個方法均為靜態同步方法,一個 Number 物件? //one two
* 7. 一個靜態同步方法,一個非靜態同步方法,兩個 Number 物件? //two one
* 8. 兩個靜態同步方法,兩個 Number 物件? //one two
*
* 執行緒八鎖的關鍵:
* ①非靜態方法的鎖預設為 this, 靜態方法的鎖為 對應的 Class 例項
* ②某一個時刻內,只能有一個執行緒持有鎖,無論幾個方法。
*/
public class TestThread8Monitor {
public static void main(String[] args) {
Number number = new Number();
Number number2 = new Number();
new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// number.getTwo();
number2.getTwo();
}
}).start();
/*new Thread(new Runnable() {
@Override
public void run() {
number.getThree();
}
}).start();*/
}
}
class Number{
public static synchronized void getOne(){//Number.class
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("one");
}
public synchronized void getTwo(){//this
System.out.println("two");
}
public void getThree(){
System.out.println("three");
}
}
11.-執行緒池與執行緒排程
執行緒池及其類似與JDBC中的資料庫連線池!!!
執行緒池是第四種獲取執行緒的方法:ExecutorService,它使用可能的幾個池執行緒之一執行每個提交的任務,通常使用Executors工廠方法配置,建立執行緒池物件。
由於減少了每個任務呼叫的開銷,它們通常可以在執行大量非同步任務時提供增強的效能,並且還可以提供繫結和管理資源(包括執行任務集時使用的執行緒)的方法。每個 ThreadPoolExecutor 還維護著一些基本的統計資料,如完成的任務數。
為了便於跨大量上下文使用,此類提供了很多可調整的引數和擴充套件鉤子 (hook)。但是,強烈建議程式設計師使用較為方便的 Executors 工廠方法 :
Executors.newCachedThreadPool()(無界執行緒池,可以進行自動執行緒回收)
Executors.newFixedThreadPool(int)(固定大小執行緒池)
Executors.newSingleThreadExecutor()(單個後臺執行緒)
它們均為大多數使用場景預定義了設定。
二、執行緒池的體系結構: * java.util.concurrent.Executor : 負責執行緒的使用與排程的根介面 * |--**ExecutorService 子介面: 執行緒池的主要介面 * |--ThreadPoolExecutor 執行緒池的實現類 * |--ScheduledExecutorService 子介面:負責執行緒的排程 * |--ScheduledThreadPoolExecutor :繼承 ThreadPoolExecutor, 實現 ScheduledExecutorService
* 三、工具類 : Executors * ExecutorService newFixedThreadPool() : 建立固定大小的執行緒池 * ExecutorService newCachedThreadPool() : 快取執行緒池,執行緒池的數量不固定,可以根據需求自動的更改數量。 * ExecutorService newSingleThreadExecutor() : 建立單個執行緒池。執行緒池中只有一個執行緒 * * ScheduledExecutorService newScheduledThreadPool() : 建立固定大小的執行緒,可以延遲或定時的執行任務。
執行緒排程:一個 ExecutorService,可安排在給定的延遲後執行或定期執行的命令。
package com.atguigu.juc;
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;
/*
* 一、執行緒池:提供了一個執行緒佇列,佇列中儲存著所有等待狀態的執行緒。避免了建立與銷燬額外開銷,提高了響應的速度。
*
* 二、執行緒池的體系結構:
* java.util.concurrent.Executor : 負責執行緒的使用與排程的根介面
* |--**ExecutorService 子介面: 執行緒池的主要介面
* |--ThreadPoolExecutor 執行緒池的實現類
* |--ScheduledExecutorService 子介面:負責執行緒的排程
* |--ScheduledThreadPoolExecutor :繼承 ThreadPoolExecutor, 實現 ScheduledExecutorService
*
* 三、工具類 : Executors
* ExecutorService newFixedThreadPool() : 建立固定大小的執行緒池
* ExecutorService newCachedThreadPool() : 快取執行緒池,執行緒池的數量不固定,可以根據需求自動的更改數量。
* ExecutorService newSingleThreadExecutor() : 建立單個執行緒池。執行緒池中只有一個執行緒
*
* ScheduledExecutorService newScheduledThreadPool() : 建立固定大小的執行緒,可以延遲或定時的執行任務。
*/
public class TestThreadPool {
public static void main(String[] args) throws Exception {
//1. 建立執行緒池
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. 為執行緒池中的執行緒分配任務
for (int i = 0; i < 10; i++) {
pool.submit(tpd);
}
//3. 關閉執行緒池
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++);
}
}
}