JUC

volatile 關鍵字和內存可見性:
內存可見性(Memory Visibility)是指當①某個線程正在使用對象狀態,而②另外一個線程在同時修改該狀態,須要③確保當一個線程修改了對象
狀態後,④其餘線程可以看到發生的狀態變化。-----java

在程序運行的時候,jvm會爲每一個線程分配一塊獨立的緩存,其中有一塊主存(存放着共享資源),線程一用於修改共享數據,main線程用於讀共享數據,
當線程一要對主存中共享數據的值進行改變的時候,先把共享數據讀到緩存中來,而後在修改值,修改完後,將修改好後的值寫會到主存中去,
在將修改後的值寫入主存中前,此時main線程從主存中的數據讀取出來,按理來講是先線程一執行修改數據,在main線程讀取修改後的數據,
因爲在線程一共享數據寫入主存以前就執行main線程中讀取共享數據,因此致使main線程緩存中的值是修改前的值(按代碼邏輯來講是修改後的),
此時main線程緩存中存放的值是修改前的值,當調用了比較底層的代碼(例如while(true)),執行效率很是高,高到main線程沒有機會再次去主存中
讀取一次數據(就是線程一修改後的,即邏輯正確狀況)。
內存可見性問題是,當多個線程操做共享數據時,彼此不可見(因爲首先讀取線程的數據(共享數據)是不符合代碼邏輯的,再加上某個底層代碼執行效率過高,讀取線程沒法再次去主存中獲取最新數據)算法

同步鎖能夠解決內存可見性問題,同步鎖能保證每次去刷新緩存,保證緩存中數據和主存一致。(意味着效率很是低,這會致使共享資源永遠只能被一個線程訪問)
volatile 關鍵字:當多個線程進行操做共享數據時,能夠保證內存中的數據可見。(能夠理解爲對他的操做(讀寫)就是在主存中完成的),相較於 synchronized 是一種較爲輕量級的同步策略。比鎖的效率高
(例如 class ThreadDemo implements Runnable { private volatile boolean flag = false; )緩存

注意:
1. volatile 不具有「互斥性」 當多個線程搶資源時,只有一個線程能獲取資源,其餘線程等資源被釋放了才能獲取,這就是互斥性
2. volatile 不能保證變量的「原子性」 (不可分割)安全


原子變量和CAS算法
* 1、i++ 的原子性問題:i++ 的操做實際上分爲三個步驟「讀-改-寫」
* int i = 10;
* i = i++; //10
*
* int temp = i;
* i = i + 1;
* i = temp;
其實i++是分爲三步,當多個線程對共享數據進行該修改操做時,他們均可以同時獲取到這個共享數據,若是同時進行修改可能出現線程一讀取完還沒寫入主存的共享數據時,其餘線程也讀取了,這會致使數據邏輯異常。
若是對共享資源加上volatile也是沒用的,他不能保證變量的原子性,由於這行代碼分爲三個操做,volatile只能保證單個操做時線程緩存中的數據和主存一致(可認爲),當多個操做時,可能線程一在執行第二個操做(改),
這時線程二去讀共享數據,從而致使數據邏輯異常。說白了就是有些java語句包含多個操做,這些操做在業務邏輯上應該是一致的(一步完成),可是在併發時沒法保證在這個一致操做,從而致使原子變量的原子性沒法保證。

*
* 2、原子變量:在 java.util.concurrent.atomic 包下提供了一些原子變量。
* 1. volatile 保證內存可見性
* 2. CAS(Compare-And-Swap) 算法保證數據變量的原子性
* CAS 算法是硬件對於併發操做的支持
* CAS 包含了三個操做數:
* ①內存值 V 初次獲取的共享數據的值
* ②預估值 A 在修改替換時向主存或者緩存中獲取的值
* ③更新值 B
* 當且僅當 V == A 時, V = B; 不然,不會執行任何操做。 這裏能夠把①當作是一個操做,②和③是同一個操做

解決方法:使用java.util.concurrent.atomic 包下提供了一些原子操做的經常使用類: 這些類知足原子變量的原子性,可直接使用
class AtomicDemo implements Runnable{
private AtomicInteger serialNumber = new AtomicInteger(0);
public int getSerialNumber(){
return serialNumber.getAndIncrement(); 至關於一個int類型的值a++
}

模擬CAS算法
package com.atguigu.juc;
/*
* 模擬 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;
//獲取內存值
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;
}
//設置
public synchronized boolean compareAndSet(int expectedValue, int newValue){
return expectedValue == compareAndSwap(expectedValue, newValue);
}
}數據結構

ConcurrentHashMap 鎖分段機制:
複合操做:
「若不存在則添加」
「若存在則刪除」
if(!table.contants()){
table.put();
}
Hashtable(這個線程安全是把整個map集合給鎖住了)在這些操做面前也是線程不安全的

ConcurrentHashMap怎麼用,map怎麼用ConcurrentHashMap就怎麼用,在1.9後使用的是CAS算法
ConcurrentHashMap 同步容器類是Java 5 增長的一個線程安全的哈希表。對與多線程的操做,介於 HashMap 與 Hashtable 之間。內部採用「鎖分段」機制替代 Hashtable 的獨佔鎖。進而提升性能。

Collections這個工具類可使得線程不安全的變成安全的原理是在集合類的每一個方法面前加個synchronize,變成同步方法,變成線程安全的了。

CopyOnWriteArrayList在每次寫入時,在底層完成一個複製,複製一個新的列表在修改,和其餘操做互相不干涉,併發修改異常不會發生,效率會低下,添加操做多時不適合選這個;迭代操做多時,於此有併發迭代時選擇這個。多線程


CountDownLatch 閉鎖:
閉鎖能夠延遲線程的進度直到其到達終止狀態,閉鎖能夠用來確保某些活動直到其餘活動都完成才繼續執行:
 確保某個計算在其須要的全部資源都被初始化以後才繼續執行;
 確保某個服務在其依賴的全部其餘服務都已經啓動以後才啓動;
 等待直到某個操做全部參與者都準備就緒再繼續執行。
/*
* CountDownLatch :閉鎖,在完成某些運算是,只有其餘全部線程的運算所有完成,當前運算才繼續執行
*這裏能夠設定多個Runnable實現類(線程執行),可是這些Runnable實現類(多個線程)共用一個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();
}
//這裏是用閉鎖使得當前線程中止,直到latch的計數到0爲止,在執行當前線程
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();//這裏用於標記當前線程已經執行完畢,那麼閉鎖減一
}
}
}併發


實現 Callable 接口:
/*
* 1、建立執行線程的方式三:實現 Callable 接口。 相較於實現 Runnable 接口的方式,方法能夠有返回值,而且能夠拋出異常。
*
* 2、執行 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); //將線程資源放入FutureTask中,返回FutureTask,這個是返回的線程執行結果
new Thread(result).start();
//2.接收線程運算後的結果
try {
Integer sum = result.get(); //FutureTask 可用於 閉鎖,只有當對應的線程資源(實現了Callable的類)執行完了後,才能夠經過result調用get方法返回返回值,若該線程未完成,當前線程處於等待。
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;
}
}dom


Lock 同步鎖:
ReentrantLock 實現了 Lock 接口,並提供了與synchronized 相同的互斥性和內存可見性。但相較於synchronized 提供了更高的處理鎖的靈活性。
/*
* 1、用於解決多線程安全問題的方式:
*
* 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(); //釋放鎖
}
}
}
}jvm


Condition 控制線程通訊:
wait():致使當前線程等待,直到其餘線程調用該同步監視器的notify()方法或notifyAll()方法來喚醒該線程,調用wait()方法的當前線程會釋放對該同步監視器的鎖定。
notify():喚醒在此同步監視器上等待的單個線程。若是全部線程都在此同步監視器上等待,則會選擇喚醒其中一個線程。選擇是任意性的。只有當前線程放棄對該同步監視器的鎖定後(使用wait()方法),才能夠執行被喚醒的線程。
notifyAll():喚醒在此同步監視器上等待的全部線程。只有當前線程放棄對該同步監視器的鎖定後(使用wait()方法),才能夠執行被喚醒的線程。
/*
* 生產者和消費者案例
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk(); //線程執行時共享的資源,通常同步方法都在共享資源裏面定義,同時調用wait(),notify(),notifyAll()都是同步監視器調用,也是在共享資源裏面(同步代碼塊,同步方法)調用
Productor pro = new Productor(clerk); //線程執行內容(實現Runnable接口)
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();//好比說這裏等待的話,在同步方法裏面,this就是同步監視器,要讓其等待的話就用同步監視器的wait()方法
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + --product);
this.notifyAll();//好比說這裏喚醒的話,在同步方法裏面,this就是同步監視器,喚醒的話必須使用同步監視器的notify(),notifyAll()方法
}
}
//生產者
class Productor implements Runnable{
private Clerk clerk;ide

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();
}
}
}


Condition 控制線程通訊:使用同步鎖
以上面的例子爲例:
僅僅修改了Clerk類
class Clerk {
private int product = 0;
private Lock lock = new ReentrantLock(); //在共享資源須要用到鎖的類定義鎖對象ReentrantLock
private Condition condition = lock.newCondition(); //定義控制方法鎖對象的Condition類
// 進貨
public void get() {
lock.lock(); // 上鎖 和釋放鎖的地方至關定義同步代碼塊
try {
while(product >= 1) { // 爲了不虛假喚醒,應該老是使用在循環中。
System.out.println("產品已滿!");
try {
condition.await(); //使用condition.await()取代同步監視器.await()方法
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ ++product);
condition.signalAll(); //使用condition.signalAll()取代同步監視器.notifyAll()方法
} finally {
lock.unlock();//釋放鎖
}
}
// 賣貨
public void sale() {
lock.lock(); // 上鎖 和釋放鎖的地方至關定義同步代碼塊
try {
while(product <= 0) {
System.out.println("缺貨!");
try {
condition.await(); //使用condition.await()取代同步監視器.await()方法
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ --product);
condition.signalAll(); //使用condition.signalAll()取代同步監視器.notifyAll()方法
} finally {
lock.unlock();//釋放鎖
}
}
}


線程按序交替:
/*
* 編寫一個程序,開啓 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() { //執行loopA方法20次
@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. 判斷 number表示接下來執行的線程
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; //打印完了A線程後,告訴他下個執行的線程是B
condition2.signal(); //這裏他只會去喚醒condition2.await()方法鎖住的線程,也就是至關於condition2.signal()只能解決condition2.await();致使的線程等待,不能解決condition3.await()
} 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();
}
}
}
Condition對象的理解:在Lock對象時,用Lock來上鎖,用Lock解鎖,Condition對象是來源於lock.newCondition()的對象,Condition是能夠在Lock來上鎖(lock.lock())後(還沒調用lock.unlock())去調用condition1.await(),
那麼在調用condition1.await()方法的線程中,該線程會釋放對該同步監視器的鎖定,只有當調用condition1.signal()才能喚醒該線程,喚醒該線程意味着該線程能夠從新得到同步監視器,繼續執行下面的代碼。
來自於同一個Lock對象的Condition對象,是能夠在Lock上鎖後,釋放鎖前,去用Condition對象去調用await()和signal()方法,即便Condition對象不一樣也行。
被喚醒的線程,他出於等待資源的狀態,能夠隨時執行,若執行下條語句處於同步鎖之中,那麼他一樣會獲取同步監視器。
注意:condition1.await()可使得當前線程處於沉睡狀態,condition1.signal()只能使指定的線程(調用condition1.await()使它沉睡的線程)喚醒。
this.signal()方法和condition.signal()方法的不一樣之處:this.signal()隨機喚醒線程(調用過this.await()方法的線程),而condition1.signal()只能喚醒線程(調用過ondition1.await()方法的線程),這也是Condition優點所在,
還有能夠有多個condition對象依賴在同一個lock對象上,使得多個不一樣級別的線程能夠在同一共享資源上面實現隨意調度(指定他們的執行順序)。

ReadWriteLock 讀寫鎖:
-ReadWriteLock 維護了一對相關的鎖,一個用於只讀操做,另外一個用於寫入操做。只要沒有 writer,讀取鎖能夠由多個 reader 線程同時保持。寫入鎖是獨佔的。
-ReadWriteLock 讀取操做一般不會改變共享資源,但執行寫入操做時,必須獨佔方式來獲取鎖。對於讀取操做佔多數的數據結構。
ReadWriteLock 能提供比獨佔鎖更高的併發性。而對於只讀的數據結構,其中包含的不變性能夠徹底不須要考慮加鎖操做。
/*
* 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();
}
}
}


線程八鎖:
• 一個對象裏面若是有多個synchronized方法,某一個時刻內,只要一個線程去調用其中的一個synchronized方法了,其它的線程都只能等待,換句話說,某一個時刻
內,只能有惟一一個線程去訪問這些synchronized方法
• 鎖的是當前對象this,被鎖定後,其它的線程都不能進入到當前對象的其它的synchronized方法
• 加個普通方法後發現和同步鎖無關
• 換成兩個對象後,不是同一把鎖了,狀況馬上變化。
• 都換成靜態同步方法後,狀況又變化
• 全部的非靜態同步方法用的都是同一把鎖——實例對象自己,也就是說若是一個實例對象的非靜態同步方法獲取鎖後,該實例對象的其餘非靜態同步方法必須等待獲
取鎖的方法釋放鎖後才能獲取鎖,但是別的實例對象的非靜態同步方法由於跟該實例對象的非靜態同步方法用的是不一樣的鎖,因此毋須等待該實例對象已獲取鎖的非
靜態同步方法釋放鎖就能夠獲取他們本身的鎖。
• 全部的靜態同步方法用的也是同一把鎖——類對象自己,這兩把鎖是兩個不一樣的對象,因此靜態同步方法與非靜態同步方法之間是不會有競態條件的。可是一旦一個
靜態同步方法獲取鎖後,其餘的靜態同步方法都必須等待該方法釋放鎖後才能獲取鎖,而不論是同一個實例對象的靜態同步方法之間,仍是不一樣的實例對象的靜態同
步方法之間,只要它們同一個類的實例對象!
/*
* 題目:判斷打印的 "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");
}
}

線程池:
/*
* 1、線程池:提供了一個線程隊列,隊列中保存着全部等待狀態的線程。避免了建立與銷燬額外開銷,提升了響應的速度。
*
* 2、線程池的體系結構:
* java.util.concurrent.Executor : 負責線程的使用與調度的根接口
* |--**ExecutorService 子接口: 線程池的主要接口
* |--ThreadPoolExecutor 線程池的實現類
* |--ScheduledExecutorService 子接口:負責線程的調度
* |--ScheduledThreadPoolExecutor :繼承 ThreadPoolExecutor, 實現 ScheduledExecutorService
*
* 3、工具類 : 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<>(); //建立一個list用於存放線程返回值
for (int i = 0; i < 10; i++) { //循環建立線程而且放入線程池中,並將線程結果值返回,而後把線程結果值統一放入list中去
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); //將返回值放入list中
}
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++);
}
}
}

線程調度:/* * 1、線程池:提供了一個線程隊列,隊列中保存着全部等待狀態的線程。避免了建立與銷燬額外開銷,提升了響應的速度。 * * 2、線程池的體系結構: * java.util.concurrent.Executor : 負責線程的使用與調度的根接口 * |--**ExecutorService 子接口: 線程池的主要接口 * |--ThreadPoolExecutor 線程池的實現類 * |--ScheduledExecutorService 子接口:負責線程的調度 * |--ScheduledThreadPoolExecutor :繼承 ThreadPoolExecutor, 實現 ScheduledExecutorService * * 3、工具類 : Executors * ExecutorService newFixedThreadPool() : 建立固定大小的線程池 * ExecutorService newCachedThreadPool() : 緩存線程池,線程池的數量不固定,能夠根據需求自動的更改數量。 * ExecutorService newSingleThreadExecutor() : 建立單個線程池。線程池中只有一個線程 * * ScheduledExecutorService newScheduledThreadPool() : 建立固定大小的線程,能夠延遲或定時的執行任務。 */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);//生成隨機數 System.out.println(Thread.currentThread().getName() + " : " + num); return num; } }, 1, TimeUnit.SECONDS); // 設置延遲多長時間執行該線程,自建立線程開始算,設置多少,就會延遲多長時間執行線程中的內容。 System.out.println(result.get()); } pool.shutdown(); }}

相關文章
相關標籤/搜索