成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【J2SE】java并發(fā)基礎(chǔ)

tianyu / 1984人閱讀

摘要:的線程機(jī)制是搶占式。會(huì)讓出當(dāng)多個(gè)線程并發(fā)的對(duì)主存中的數(shù)據(jù)進(jìn)行操作時(shí),有且只有一個(gè)會(huì)成功,其余均失敗。和對(duì)象只有在困難的多線程問(wèn)題中才是必須的。

并發(fā)簡(jiǎn)述

并發(fā)通常是用于提高運(yùn)行在單處理器上的程序的性能。在單 CPU 機(jī)器上使用多任務(wù)的程序在任意時(shí)刻只在執(zhí)行一項(xiàng)工作。

并發(fā)編程使得一個(gè)程序可以被劃分為多個(gè)分離的、獨(dú)立的任務(wù)。一個(gè)線程就是在進(jìn)程中的一個(gè)單一的順序控制流。java的線程機(jī)制是搶占式。

線程的好處是提供了輕量級(jí)的執(zhí)行上下文切換,只改變了程序的執(zhí)行序列和局部變量。

多線程的主要缺陷:

等待共享資源的時(shí)候性能降低。

需要處理線程的額外 CPU 花費(fèi)。

糟糕的程序設(shè)計(jì)導(dǎo)致不必要的復(fù)雜度。

有可能產(chǎn)生一些病態(tài)行為,若餓死、競(jìng)爭(zhēng)、死鎖和活鎖。

不同平臺(tái)導(dǎo)致的不一樣。

volatile關(guān)鍵字

源來(lái)

當(dāng)程序運(yùn)行,JVM會(huì)為每一個(gè)線程分配一個(gè)獨(dú)立的緩存用于提高執(zhí)行效率,每一個(gè)線程都在自己獨(dú)立的緩存中操作各自的數(shù)據(jù)。一個(gè)線程在緩沖中對(duì)數(shù)據(jù)進(jìn)行修改,寫入到主存后,其他線程無(wú)法得知數(shù)據(jù)已被更改,仍在操作緩存中已過(guò)時(shí)的數(shù)據(jù),為了解決這個(gè)問(wèn)題,提供了volatile關(guān)鍵字,實(shí)現(xiàn)內(nèi)存可見,一旦主存數(shù)據(jù)被修改,便致使其他線程緩存數(shù)據(jù)行無(wú)效,強(qiáng)制前往主存獲取新數(shù)據(jù)。

Example:內(nèi)存不可見,導(dǎo)致主線程無(wú)法結(jié)束。

class ThreadDemo implements Runnable {
    //添加volatile關(guān)鍵字可實(shí)現(xiàn)內(nèi)存可見性 public volatile boolean flag = false;
    public boolean flag = Boolean.false;
    
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
        }
        flag = Boolean.true;
        System.out.println("ThreadDemo over");
    }
    
    public boolean isFlag() {
        return flag;
    }
}

public class TestVolatile {

    public static void main(String[] args) {
        ThreadDemo demo = new ThreadDemo();
        new Thread(demo).start();
        
        while (true) {
            if (demo.flag || demo.isFlag()) {
                System.out.println("Main over");
                break;
            }
        }
    }
}/*output:打印ThreadDemo over,主線程持續(xù)循環(huán)*/

作用:

當(dāng)多個(gè)線程操作共享數(shù)據(jù)時(shí),保證內(nèi)存中的數(shù)據(jù)可見性。采用底層的內(nèi)存柵欄,及時(shí)的將緩存中修改的數(shù)據(jù)刷新到主存中,并導(dǎo)致其他線程所緩存的數(shù)據(jù)無(wú)效,使得這些線程必須去主存中獲取修改的數(shù)據(jù)。

優(yōu)缺點(diǎn):

保證內(nèi)存可見性,讓各個(gè)線程能夠彼此獲取最新的內(nèi)存數(shù)據(jù)。

較傳統(tǒng)synchronized加鎖操作提高了效率,若有線程正在操作被synchronized修飾的代碼塊數(shù)據(jù)時(shí),其他線程試圖進(jìn)行操作,發(fā)現(xiàn)已被其他線程占用,試圖操作的線程必須掛起,等到下一次繼續(xù)嘗試操作。

對(duì)volatile修飾的數(shù)據(jù)被修改后,其他線程必須前往主存中讀取,若修改頻繁,需要不斷讀取主存數(shù)據(jù),效率將會(huì)降低。

使用volatile,底層采用內(nèi)存柵欄,JVM將不會(huì)對(duì)其提供指令重排序及其優(yōu)化。

不具備互斥性。多個(gè)線程可以同時(shí)對(duì)數(shù)據(jù)進(jìn)行操作,只是由原來(lái)的在緩存操作轉(zhuǎn)變成了直接在主存中操作。(synchronized是互斥的,一個(gè)線程正在執(zhí)行,其他線程必須掛起等待)

不保證變量的原子性。使用volatile僅僅是一個(gè)能保證可見性的輕量級(jí)同步策略。

原子變量與 CAS 算法

Example:使用volatile修飾,number自增問(wèn)題。

class ThreadDemo implements Runnable {
    public volatile int number = 0;
    
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (Exception e) {
        }
        System.out.print(getIncrementNumber() + " ");
    }
    
    public int getIncrementNumber() {
        return ++number;
    }
}

public class TestAtomic {
    public static void main(String[] args) {
        ThreadDemo demo = new ThreadDemo();
        for (int i = 0; i < 10; i++) {
            new Thread(demo).start();
        }
    }
}/*output: 1 5 4 7 3 9 2 1 8 6 */
//    ++number底層原理思想
int temp = number;        // ①
number = number + 1;    // ②
temp = number;            // ③
return temp;            // ④

由 ++number 可知,返回的是 temp 中存儲(chǔ)的值,且自增是一個(gè)多步操作,當(dāng)多個(gè)線程調(diào)用 incrementNumber方法時(shí),方法去主存中獲取 number 值放入 temp 中,根據(jù) CPU 時(shí)間片切換,當(dāng) A 線程完成了 ③ 操作時(shí),時(shí)間片到了被中斷,A 線程開始執(zhí)行 ① 時(shí)不幸被中斷,接著 A 獲取到了CPU執(zhí)行權(quán),繼續(xù)執(zhí)行完成 ④ 操作更新了主存中的值,緊接著 B 線程開始執(zhí)行,但是 B 線程 temp中存儲(chǔ)的值已經(jīng)過(guò)時(shí)了。注意:自增操作為四步,只有在第四步的時(shí)候才會(huì)刷新主存的值,而不是number = number + 1 操作就反映到主存中去。如圖所示:

源來(lái):

volatile只能保證內(nèi)存可見性,對(duì)多步操作的變量,無(wú)法保證其原子性,為了解決這個(gè)問(wèn)題,提供了原子變量。

作用:

原子變量既含有volatile的內(nèi)存可見性,又提供了對(duì)變量原子性操作的支持,采用底層硬件對(duì)并發(fā)操作共享數(shù)據(jù)的 CAS(Compare-And-Swap)算法,保證數(shù)據(jù)的原子性。

提供的原子類:

描述
AtomicBoolean 一個(gè) boolean值可以用原子更新。
AtomicInteger 可能原子更新的 int值。
AtomicIntegerArray 一個(gè) int數(shù)組,其中元素可以原子更新。
AtomicIntegerFieldUpdater 基于反射的實(shí)用程序,可以對(duì)指定類的指定的 volatile int字段進(jìn)行原子更新。
AtomicLong 一個(gè) long值可以用原子更新。
AtomicLongArray 可以 long地更新元素的 long數(shù)組。
AtomicLongFieldUpdater 基于反射的實(shí)用程序,可以對(duì)指定類的指定的 volatile long字段進(jìn)行原子更新。
AtomicMarkableReference AtomicMarkableReference維護(hù)一個(gè)對(duì)象引用以及可以原子更新的標(biāo)記位。
AtomicReference 可以原子更新的對(duì)象引用。
AtomicReferenceArray 可以以原子方式更新元素的對(duì)象引用數(shù)組。
AtomicReferenceFieldUpdater 一種基于反射的實(shí)用程序,可以對(duì)指定類的指定的 volatile volatile引用原子更新。
AtomicStampedReference AtomicStampedReference維護(hù)對(duì)象引用以及可以原子更新的整數(shù)“印記”。
DoubleAccumulator 一個(gè)或多個(gè)變量一起維護(hù)使用提供的功能更新的運(yùn)行的值 double 。
DoubleAdder 一個(gè)或多個(gè)變量一起保持初始為零 double和。
LongAccumulator 一個(gè)或多個(gè)變量,它們一起保持運(yùn)行 long使用所提供的功能更新值。
LongAdder 一個(gè)或多個(gè)變量一起保持初始為零 long總和。

CAS算法:

CAS(Compare-And-Swap)是底層硬件對(duì)于原子操作的一種算法,其包含了三個(gè)操作數(shù):內(nèi)存值(V),預(yù)估值(A),更新值(B)。當(dāng)且僅當(dāng) V == A 時(shí), 執(zhí)行 V = B 操作;否則不執(zhí)行任何結(jié)果。這里需要注意,A 和 B 兩個(gè)操作數(shù)是原子性的,同一時(shí)刻只能有一個(gè)線程進(jìn)行AB操作。

優(yōu)缺點(diǎn):

操作失敗時(shí),直接放棄結(jié)果,并不釋放對(duì)CPU的控制權(quán),進(jìn)而可以繼續(xù)嘗試操作,不必掛起等待。(synchronized會(huì)讓出CPU)

當(dāng)多個(gè)線程并發(fā)的對(duì)主存中的數(shù)據(jù)進(jìn)行操作時(shí),有且只有一個(gè)會(huì)成功,其余均失敗。

原子變量中封裝了用于對(duì)數(shù)據(jù)的原子操作,簡(jiǎn)化了代碼的編寫。

Collection并發(fā)類

HashMap 與 HashTable簡(jiǎn)述

HashMap是線程不安全的,而HashTable是線程安全的,因?yàn)镠ashTable所維護(hù)的Hash表存在著獨(dú)占鎖,當(dāng)多個(gè)線程并發(fā)訪問(wèn)時(shí),只能有一個(gè)線程可進(jìn)行操作,但是對(duì)于復(fù)合操作時(shí),HashTable仍然存在線程安全問(wèn)題,不使用HashTable的主要原因還是效率低下。

// 功能:不包含obj,則添加
if (!hashTable.contains(obj)) {
    // 復(fù)合操作,執(zhí)行此處時(shí)線程中斷,obj被其他線程添加至容器中,此處繼續(xù)執(zhí)行將導(dǎo)致重復(fù)添加
    hashTable.put(obj);
}

可知上述兩個(gè)操作需要 “原子性”,為了達(dá)到效果,還不是得對(duì)代碼塊進(jìn)行同步

ConcurrentHashMap

采用鎖分段機(jī)制,分為 16 個(gè)段(并發(fā)級(jí)別),每一個(gè)段下有一張表,該表采用鏈表結(jié)構(gòu)鏈接著各個(gè)元素,每個(gè)段都使用獨(dú)立的鎖。當(dāng)多個(gè)線程并發(fā)操作的時(shí)候,根據(jù)各自的級(jí)別不同,操作不同的段,多個(gè)線程并行操作,明顯提高了效率,其次還提供了復(fù)合操作的諸多方法。注:jdk1.8由原來(lái)的數(shù)組+單向鏈表結(jié)構(gòu)轉(zhuǎn)換成數(shù)據(jù)+單向鏈表+紅黑樹結(jié)構(gòu)。

ConcurrentSkipListMap和ConcurrentSkipListSet

有序的哈希表,通過(guò)跳表實(shí)現(xiàn),不允許null作為鍵或值。ConcurrentSkipListMap詳解

CopyOnWriteArrayList 和 CopyOnWriteArraySet

對(duì)collection進(jìn)行寫入操作時(shí),將導(dǎo)致創(chuàng)建整個(gè)底層數(shù)組的副本,而源數(shù)組將保留在原地,使得復(fù)制的數(shù)組在被修改時(shí),讀取操作可以安全的執(zhí)行。當(dāng)修改完成時(shí),一個(gè)原子性的操作將把心的數(shù)組換人,使得新的讀取操作可以看到新的修改。

好處之一是當(dāng)多個(gè)迭代器同時(shí)遍歷和修改列表時(shí),不會(huì)拋出ConcurrentModificationException。

小結(jié):

當(dāng)期望許多線程訪問(wèn)一個(gè)給定 collection 時(shí),ConcurrentHashMap 通常優(yōu)于同步的 HashMap

ConcurrentSkipListMap 通常優(yōu)于同步的 TreeMap。

當(dāng)期望的讀數(shù)和遍歷遠(yuǎn)遠(yuǎn)大于列表的更新數(shù)時(shí),CopyOnWriteArrayList 優(yōu)于同步的 ArrayList。

并發(fā)迭代操作多時(shí),可選擇CopyOnWriteArrayList 和 CopyOnWriteArraySet。

高并發(fā)情況下,可選擇ConcurrentSkipListMap和ConcurrentSkipListSet

CountDownLatch閉鎖

源由:

當(dāng)一個(gè)修房子的 A 線程正在執(zhí)行,需要磚頭時(shí),開啟了一個(gè)線程 B 去拉磚頭,此時(shí) A 線程需要等待 B 線程的結(jié)果后才能繼續(xù)執(zhí)行時(shí),但是線程之間都是并行操作的,為了解決這個(gè)問(wèn)題,提供了CountDownLatch。

作用:

一個(gè)同步輔助類,為了保證執(zhí)行某些操作時(shí),“所有準(zhǔn)備事項(xiàng)都已就緒”,僅當(dāng)某些操作執(zhí)行完畢后,才能執(zhí)行后續(xù)的代碼塊,否則一直等待。

CountDownLatch中存在一個(gè)鎖計(jì)數(shù)器,如果鎖計(jì)數(shù)器不為 0 的話,它會(huì)阻塞任何一個(gè)調(diào)用 await() 方法的線程。也就是說(shuō),當(dāng)一個(gè)線程調(diào)用 await() 方法時(shí),如果鎖計(jì)數(shù)器不等于 0,那么就會(huì)一直等待鎖計(jì)數(shù)器為 0 的那一刻,這樣就解決了需要等待其他線程執(zhí)行完畢才執(zhí)行的需求。

Example:

class ThreadDemo implements Runnable {
    private CountDownLatch latch = null;

    public ThreadDemo(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            System.out.println("execute over");
        } finally {
            latch.countDown();    // 必須保證計(jì)數(shù)器減一
        }
    }
}
public class TestCountDownLatch {

    public static void main(String[] args) {
        final int count = 10;
        final CountDownLatch latch = new CountDownLatch(count);
        ThreadDemo demo = new ThreadDemo(latch);
        for (int i = 0; i < count; ++i) {
            new Thread(demo).start();
        }
        
        try {
            latch.await();    // 等待計(jì)數(shù)器為 0
            System.out.println("其他線程結(jié)束,繼續(xù)往下執(zhí)行...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}/**output:
    execute over
    ...
    其他線程結(jié)束,繼續(xù)往下執(zhí)行...
*/

細(xì)節(jié):

子線程完畢后,必須調(diào)用 countDown() 方法使得 鎖計(jì)數(shù)器減一,否則將會(huì)導(dǎo)致調(diào)用 await() 方法的線程持續(xù)等待,盡可能的放置在 finally 中。

鎖計(jì)數(shù)器的個(gè)數(shù)與子線程數(shù)最好相等,只要計(jì)數(shù)器等于 0,不論是否還存在子線程,await() 方法將得到響應(yīng),繼續(xù)執(zhí)行后續(xù)代碼。

Callable接口

源由:

當(dāng)開啟一個(gè)線程執(zhí)行運(yùn)算時(shí),可能會(huì)需要該線程的計(jì)算結(jié)果,之前的 implements Runnableextends Thread 的 run() 方法并沒有提供可以返回的功能,因此提供了 Callable接口。 Callable 的運(yùn)行結(jié)果, 需要使用 FutureTask 類來(lái)接受。

Example:

class ThreadDemo implements Callable {
    private Integer cycleValue;
    
    public ThreadDemo(Integer cycleValue) {
        this.cycleValue = cycleValue;
    }
    
    @Override
    public Integer call() throws Exception {
        int result = 0;
        for (int i=0; i task = new FutureTask<>(demo);
        new Thread(task).start();
        
        Integer result = task.get();    // 等待計(jì)算結(jié)果返回, 閉鎖
        System.out.println(result);
    }
}/*output:1073741825 */
Lock同步鎖和Condition線程通信控制對(duì)象
Lock:在進(jìn)行性能測(cè)試時(shí),使用Lock通常會(huì)比使用synchronized要高效許多,并且synchronized的開銷變化范圍很大,而Lock相對(duì)穩(wěn)定。只有在性能調(diào)優(yōu)時(shí)才使用Lock對(duì)象。

Condition: 替代了 Object 監(jiān)視器方法的使用,描述了可能會(huì)與鎖有關(guān)的條件標(biāo)量,相比 Object 的 notifyAll() ,Condition 的 signalAll() 更安全。Condition 實(shí)質(zhì)上被綁定到一個(gè)鎖上,使用newCondition() 方法為 Lock 實(shí)例獲取 Condition。

Lock和Condition對(duì)象只有在困難的多線程問(wèn)題中才是必須的。

synchonized與Lock的區(qū)別:

synchonized Lock
隱式鎖 顯示鎖
JVM底層實(shí)現(xiàn),由JVM維護(hù) 由程序員手動(dòng)維護(hù)
靈活控制(也有風(fēng)險(xiǎn))

“虛假喚醒”:當(dāng)一個(gè)線程A在等待時(shí),被另一個(gè)線程喚醒,被喚醒的線程不一定滿足了可繼續(xù)向下執(zhí)行的條件,如果被喚醒的線程未滿足條件,而又向下執(zhí)行了,那么稱這個(gè)現(xiàn)象為 “虛假喚醒”。

//    安全的方式,保證退出等待循環(huán)前,一定能滿足條件
while (條件) {
    wait();
}

Example:生產(chǎn)消費(fèi)者

// 產(chǎn)品car
class Car {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean available = false; // false:無(wú)貨;true有貨

    public void put(){
        lock.lock();
        try {
            while (available) {        // 有貨等待
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + "put():    進(jìn)貨");
            available = true;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void get() {
        lock.lock();
        try {
            while (!available) {    // 無(wú)貨等待
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + "get():出貨");
            available = false;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}
// 消費(fèi)者
class Consume implements Runnable {
    private Car car;
    
    public Consume(Car car) {
        this.car = car;
    }

    @Override
    public void run() {
        for (int i=0; i
每一個(gè) 對(duì)lock()的調(diào)用都必須緊跟著一個(gè) try-finally 子句,用以保證可以在任何情況下都能釋放鎖,任務(wù)在調(diào)用 await()、signal()signalAll()之前,必須擁有鎖。
lock.lock();
try {
    ...    // 業(yè)務(wù)代碼
} finally {
    lock.unlock();
}
ReadWriteLock讀寫鎖

源由:

上述講解的鎖都是讀寫一把鎖,不論是讀或?qū)懀际且话焰i解決,當(dāng)多線程訪問(wèn)數(shù)據(jù)時(shí),若發(fā)生了一千次操作,其中的寫操作只執(zhí)行了一次,數(shù)據(jù)的更新率非常低,那么每次進(jìn)行讀操作時(shí),都要加鎖讀取”不會(huì)更改的“數(shù)據(jù),顯然是不必要的開銷,因此出現(xiàn)了 ReadWriteLock 讀寫鎖,該對(duì)象提供讀鎖和寫鎖。

作用:

ReadWriteLock 維護(hù)了一對(duì)相關(guān)的鎖,一個(gè)用于只讀操作,另一個(gè)用于寫入操作。只要沒有 write寫入操作,那么多個(gè)線程可以同時(shí)進(jìn)行持有讀鎖。而寫入鎖是獨(dú)占的,當(dāng)執(zhí)行寫操作時(shí),其他線程不可寫,也不可讀。

性能的提升取決于讀寫操作期間讀取數(shù)據(jù)相對(duì)于修改數(shù)據(jù)的頻率,如果讀取操作遠(yuǎn)遠(yuǎn)大于寫入操作時(shí),便能增強(qiáng)并發(fā)性。

Example:

class Demo {
    private int value = 0;
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    
    public void read() {
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " : " + value);
        } finally {
            lock.readLock().unlock();
        }
    }
    
    public void write(int value) {
        lock.writeLock().lock();
        try {
            this.value = value;
            System.out.println("write(" + value + ")");
        } finally {
            lock.writeLock().unlock();
        }
    }
}
class ReadLock implements Runnable {
    private Demo demo = null;
    
    public ReadLock(Demo demo) {
        this.demo = demo;
    }
    
    @Override
    public void run() {
        for (int i=0; i<20; ++i) {
            demo.read();
            try {
                Thread.sleep(320);
            } catch (InterruptedException e) {
            }
        }
    }
    
}
class WriteLock implements Runnable {
    private Demo demo = null;
    
    public WriteLock(Demo demo) {
        this.demo = demo;
    }
    
    @Override
    public void run() {
        for (int i=0; i<10; ++i) {
            demo.write(i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }
        }
    }
    
}
public class TestReadWriteLock {
    
    public static void main(String[] args) {
        Demo demo = new Demo();
        ReadLock readLock = new ReadLock(demo);
        WriteLock writeLock = new WriteLock(demo);
        for (int i=0; i<3; ++i) {
            new Thread(readLock, i + "--").start();
        }
        new Thread(writeLock).start();
    }
}/**output:
    0-- : 0
    1-- : 0
    2-- : 0
    write(0)
    write(1)
    1-- : 1
    2-- : 1
    0-- : 1
    write(2)
    write(3)
    1-- : 3
    0-- : 3
    ...
*/
線程池與線程調(diào)度

源來(lái):

在傳統(tǒng)操作中(如連接數(shù)據(jù)庫(kù)),當(dāng)我們需要使用一個(gè)線程的時(shí)候,就 直接創(chuàng)建一個(gè)線程,線程完畢后被垃圾收集器回收。每一次需要線程的時(shí)候,不斷的創(chuàng)建與銷毀,大大增加了資源的開銷。

作用:

線程池維護(hù)著一個(gè)線程隊(duì)列,該隊(duì)列中保存著所有等待著的線程,避免了重復(fù)的創(chuàng)建與銷毀而帶來(lái)的開銷。

體系結(jié)構(gòu):

Execuotr:負(fù)責(zé)線程的使用與調(diào)度的根接口。
    |- ExecutorService:線程池的主要接口。
        |- ForkJoinPool:采用分而治之技術(shù)將任務(wù)分解。
        |- ThreadPoolExecutor:線程池的實(shí)現(xiàn)類。
        |- ScheduledExecutorService:負(fù)責(zé)線程調(diào)度的子接口。
            |- ScheduledThreadPoolExecutor:負(fù)責(zé)線程池的調(diào)度。繼承ThreadPoolExecutor并實(shí)現(xiàn)ScheduledExecutorService接口

Executors 工具類API描述:

方法 描述
ExecutorService newFixedThreadPool(int nThreads) 創(chuàng)建一個(gè)可重用固定數(shù)量的無(wú)界隊(duì)列線程池。使用了有限的線程集來(lái)執(zhí)行所提交的所有任務(wù)。創(chuàng)建的時(shí)候可以一次性預(yù)先進(jìn)行代價(jià)高昂的線程分配。
ExecutorService newWorkStealingPool(int parallelism) 創(chuàng)建一個(gè)維護(hù)足夠的線程以支持給定的parallelism并行級(jí)別的線程池。
ExecutorService newSingleThreadExecutor() 創(chuàng)建一個(gè)使用單個(gè)線程運(yùn)行的無(wú)界隊(duì)列的執(zhí)行程序。
ExecutorService newCachedThreadPool() 創(chuàng)建一個(gè)根據(jù)需要?jiǎng)?chuàng)建新線程的線程池,當(dāng)有可用線程時(shí)將重新使用以前構(gòu)造的線程。
ScheduledExecutorService newSingleThreadScheduledExecutor() 創(chuàng)建一個(gè)單線程執(zhí)行器,可以調(diào)度命令在給定的延遲之后運(yùn)行,或定期執(zhí)行。
ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 創(chuàng)建一個(gè)線程池,可以調(diào)度命令在給定的延遲之后運(yùn)行,或定期執(zhí)行。
ThreadFactory privilegedThreadFactory() 返回一個(gè)用于創(chuàng)建與當(dāng)前線程具有相同權(quán)限的新線程的線程工廠。

補(bǔ)充:

ExecutorService.shutdown():防止新任務(wù)被提交,并繼續(xù)運(yùn)行被調(diào)用之前所提交的所有任務(wù),待任務(wù)都完成后退出。

CachedThreadPoo在程序執(zhí)行過(guò)程中通常會(huì)創(chuàng)建與所需數(shù)量相同的線程,然后在它回收舊線程時(shí)停止創(chuàng)建新線程,是Executor的首選。僅當(dāng)這個(gè)出現(xiàn)問(wèn)題時(shí),才需切換 FixedThreadPool。

SingleThreadExecutor: 類似于線程數(shù)量為 1 的FixedThreadPool,但它提供了不會(huì)存在兩個(gè)及以上的線程被并發(fā)調(diào)用的并發(fā)。

Example:線程池

public class TestThreadPool {

    public static void main(String[] args) throws Exception {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 10; ++i) {
            Future future = pool.submit(new Callable() {

                @Override
                public String call() throws Exception {
                    return Thread.currentThread().getName();
                }
                
            });

            String threadName = future.get();
            System.out.println(threadName);
        }
        pool.shutdown();    // 拒絕新任務(wù)并等待正在執(zhí)行的線程完成當(dāng)前任務(wù)后關(guān)閉。
    }
}/**output:
    pool-1-thread-1
    pool-1-thread-2
    pool-1-thread-1
    pool-1-thread-2
    ...
*/

Example:線程調(diào)度

public class TestThreadPool {

    public static void main(String[] args) throws Exception {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
        for (int i = 0; i < 5; ++i) {
            ScheduledFuture future = pool.schedule(new Callable() {
    
                @Override
                public String call() throws Exception {
                    return Thread.currentThread().getName() + " : " + Instant.now();
                }
            }, 1, TimeUnit.SECONDS);    // 延遲執(zhí)行單位為 1秒的任務(wù)
            
            String result = future.get();
            System.out.println(result);
        }
        pool.shutdown();
    }
}/**output:
    pool-1-thread-1 : 2019-03-18T12:10:31.260Z
    pool-1-thread-1 : 2019-03-18T12:10:32.381Z
    pool-1-thread-2 : 2019-03-18T12:10:33.382Z
    pool-1-thread-1 : 2019-03-18T12:10:34.383Z
    pool-1-thread-2 : 2019-03-18T12:10:35.387Z
*/

注意:若沒有執(zhí)行 shutdown()方法,則線程會(huì)一直等待而不停止。

ForkJoinPool分支/合并框架

源由:

在一個(gè)線程隊(duì)列中,假如隊(duì)頭的線程由于某種原因?qū)е铝俗枞?,那么在該?duì)列中的后繼線程需要等待隊(duì)頭線程結(jié)束,只要隊(duì)頭一直阻塞,這個(gè)隊(duì)列中的所有線程都將等待。此時(shí),可能其他線程隊(duì)列都已經(jīng)完成了任務(wù)而空閑,這種情況下,就大大減少了吞吐量。

ForkJoin的“工作竊取”模式:

當(dāng)執(zhí)行一個(gè)新任務(wù)時(shí),采用分而治之的思想,將其分解成更小的任務(wù)執(zhí)行,并將分解的任務(wù)加入到線程隊(duì)列中,當(dāng)某一個(gè)線程隊(duì)列沒有任務(wù)時(shí),會(huì)隨機(jī)從其他線程隊(duì)列中“偷取”一個(gè)任務(wù),放入自己的隊(duì)列中執(zhí)行。

Example:

// 求次方: value為底,size為次方數(shù)
class CountPower extends RecursiveTask {
    private static final long serialVersionUID = 1L;
    public Long value = 0L;
    public int size = 0;
    public static final Long CRITICAL = 10L;     // 閾值
    
    public CountPower(Long value, int size) {
        this.value = value;
        this.size = size;
    }

    @Override
    protected Long compute() {
        // 當(dāng)要開方的此時(shí) 小于 閾值,則計(jì)算 (視為最小的任務(wù)單元)
        if(size <= CRITICAL) {
            Long sum = 1L;
            for (int i=0; i

根據(jù)分而治之的思想進(jìn)行分解,需要一個(gè)結(jié)束遞歸的條件,該條件內(nèi)的代碼就是被分解的最小單元。使用fork()在當(dāng)前任務(wù)正在運(yùn)行的池中異步執(zhí)行此任務(wù),即將該任務(wù)壓入線程隊(duì)列。調(diào)用join()`返回計(jì)算結(jié)果。RecursiveTask是有返回值的task,RecursiveAction則是沒有返回值的。

參考

尚硅谷JUC視頻教程

《java編程思想》第 21 章 并發(fā)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73784.html

相關(guān)文章

  • J2SEjava并發(fā)編程實(shí)戰(zhàn) 讀書筆記( 一、二、三章)

    摘要:發(fā)布的對(duì)象內(nèi)部狀態(tài)可能會(huì)破壞封裝性,使程序難以維持不變性條件。不變性線程安全性是不可變對(duì)象的固有屬性之一??勺儗?duì)象必須通過(guò)安全方式來(lái)發(fā)布,并且必須是線程安全的或者有某個(gè)鎖保護(hù)起來(lái)。 線程的優(yōu)缺點(diǎn) 線程是系統(tǒng)調(diào)度的基本單位。線程如果使用得當(dāng),可以有效地降低程序的開發(fā)和維護(hù)等成本,同時(shí)提升復(fù)雜應(yīng)用程序的性能。多線程程序可以通過(guò)提高處理器資源的利用率來(lái)提升系統(tǒng)的吞吐率。與此同時(shí),在線程的使用...

    QLQ 評(píng)論0 收藏0
  • 聊聊 Java8 以后各個(gè)版本的新特性

    摘要:于是抽時(shí)間看了看以后各個(gè)版本的特性,做了一個(gè)總結(jié)。年和公開版本發(fā)布,取名為。此后對(duì)應(yīng)版本就是,。發(fā)布,是一個(gè)重大版本更新。在此之后,就是每六個(gè)月發(fā)布一次新版本。以上和參考資料聊了一些關(guān)于的歷史,下面我們看看各個(gè)版本有那些新特性。 【這是 ZY 第 11 篇原創(chuàng)技術(shù)文章】 某天在網(wǎng)上閑逛,突然看到有篇介紹 Java 11 新特性的文章,頓時(shí)心里一驚,畢竟我對(duì)于 Java 的版本認(rèn)識(shí)...

    K_B_Z 評(píng)論0 收藏0
  • Java新手的一些建議——Java知識(shí)點(diǎn)歸納(Java基礎(chǔ)部分)

    摘要:中很多特性或者說(shuō)知識(shí)點(diǎn)都是和面向?qū)ο缶幊谈拍钕嚓P(guān)的。在多線程中內(nèi)容有很多,只是簡(jiǎn)單說(shuō)明一下中初步使用多線程需要掌握的知識(shí)點(diǎn),以后有機(jī)會(huì)單獨(dú)再詳細(xì)介紹一些高級(jí)特性的使用場(chǎng)景。   寫這篇文章的目的是想總結(jié)一下自己這么多年來(lái)使用java的一些心得體會(huì),主要是和一些java基礎(chǔ)知識(shí)點(diǎn)相關(guān)的,所以也希望能分享給剛剛?cè)腴T的Java程序員和打算入Java開發(fā)這個(gè)行當(dāng)?shù)臏?zhǔn)新手們,希望可以給大家一些經(jīng)...

    lykops 評(píng)論0 收藏0
  • J2SEjava NIO 基礎(chǔ)學(xué)習(xí)

    摘要:標(biāo)記,表示記錄當(dāng)前的位置。直接緩沖通過(guò)方法分配的緩沖區(qū),此緩沖區(qū)建立在物理內(nèi)存中。直接在兩個(gè)空間中開辟內(nèi)存空間,創(chuàng)建映射文件,去除了在內(nèi)核地址空間和用戶地址空間中的操作,使得直接通過(guò)物理內(nèi)存?zhèn)鬏敂?shù)據(jù)。 NIO與IO的區(qū)別 IO NIO 阻塞式 非阻塞式、選擇器selectors 面向流:?jiǎn)蜗蛄鲃?dòng),直接將數(shù)據(jù)從一方流向另一方 面向緩存:將數(shù)據(jù)放到緩存區(qū)中進(jìn)行存取,經(jīng)通道進(jìn)行...

    yexiaobai 評(píng)論0 收藏0
  • Java編程基礎(chǔ)01——計(jì)算機(jī)基礎(chǔ)

    摘要:外部存儲(chǔ)器可用于長(zhǎng)期保存大量程序和數(shù)據(jù),其成本低容量大,但速度較慢。 1_計(jì)算機(jī)概述(了解) A:什么是計(jì)算機(jī)?計(jì)算機(jī)在生活中的應(yīng)用舉例 計(jì)算機(jī)(Computer)全稱:電子計(jì)算機(jī),俗稱電腦。是一種能夠按照程序運(yùn)行,自動(dòng)、高速處理海量數(shù)據(jù)的現(xiàn)代化智能電子設(shè)備。由硬件和軟件所組成,沒有安裝任何軟件的計(jì)算機(jī)稱為裸機(jī)。常見的形式有臺(tái)式計(jì)算機(jī)、筆記本計(jì)算機(jī)、大型計(jì)算機(jī)等。 應(yīng)用舉例 ...

    xiangzhihong 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<