3.1多線程(馬士兵教育-左程云)

01.Java中線程的實現(xiàn)方式?
  1. 繼承 Thread 類讲衫,重寫其中的 run() 方法;
public class MyThread extends Thread {
    public void run() {
        System.out.println("線程開始執(zhí)行");
        // TODO: 執(zhí)行需要執(zhí)行的代碼
        System.out.println("線程執(zhí)行結(jié)束");
    }
}
# 調(diào)用
public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

2.實現(xiàn) Runnable 接口,重寫run()方法。代碼如下:

public class MyRunnable implements Runnable {
    public void run() {
        System.out.println("線程開始執(zhí)行");
        // TODO: 執(zhí)行需要執(zhí)行的代碼
        System.out.println("線程執(zhí)行結(jié)束");
    }
}
# 調(diào)用
public class Main {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

  1. 實現(xiàn)Callable接口,重寫call()方法
public class MyCallable implements Callable<Integer> {
    public Integer call() throws Exception {
        System.out.println("線程開始執(zhí)行");
        // TODO: 執(zhí)行需要執(zhí)行的代碼
        System.out.println("線程執(zhí)行結(jié)束");
        return 0;
    }
}
# 調(diào)用
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable callable = new MyCallable();
        FutureTask<Integer> task = new FutureTask<>(callable);
        Thread thread = new Thread(task);
        thread.start();
        Integer result = task.get();
    }
}

4.基于線程池實現(xiàn)茸时。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 創(chuàng)建線程池东囚,其中參數(shù)為線程池大小
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        // 創(chuàng)建Runnable任務(wù)
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
            }
        };
        // 提交任務(wù)到線程池中執(zhí)行
        for (int i = 0; i < 5; i++) {
            executorService.submit(task);
        }
        // 關(guān)閉線程池
        executorService.shutdown();
    }
}
# 結(jié)果
Thread pool-1-thread-1 is running.
Thread pool-1-thread-3 is running.
Thread pool-1-thread-2 is running.
Thread pool-1-thread-3 is running.
Thread pool-1-thread-1 is running.

總結(jié):追其底層實現(xiàn)邏輯,它只有一種實現(xiàn)方式恩急。因為Thread類實現(xiàn)的Runnable接口,Callable接口繼承了Runnable接口,線程池中的Work工作線程也是實現(xiàn)了Runnable接口抛人。

02.Java中線程的狀態(tài)?

Java中線程的狀態(tài)分為6種:

  1. 初始(NEW):新創(chuàng)建了一個線程對象,但還沒有調(diào)用start()方法脐瑰。
  2. 運行(RUNNABLE):Java線程中將就緒(ready)和運行中(running)兩種狀態(tài)籠統(tǒng)的稱為“運行”妖枚。
    線程對象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對象的start()方法苍在。該狀態(tài)的線程位于可運行線程池中绝页,等待被線程調(diào)度選中,獲取CPU的使用權(quán)寂恬,此時處于就緒狀態(tài)(ready)续誉。就緒狀態(tài)的線程在獲得CPU時間片后變?yōu)檫\行中狀態(tài)(running)。
  3. 阻塞(BLOCKED):表示線程阻塞于鎖初肉。
  4. 等待(WAITING):進入該狀態(tài)的線程需要等待其他線程做出一些特定動作(通知或中斷)酷鸦。
  5. 超時等待(TIMED_WAITING):該狀態(tài)不同于WAITING,它可以在指定的時間后自行返回。
  6. 終止(TERMINATED):表示該線程已經(jīng)執(zhí)行完畢臼隔。


    image.png
03.Java中如何停止線程?

1.使用標(biāo)記位中止線程;
2.使用 stop() 方法強行終止線程;(暴力停止)
3.使用interrupt() 方法中斷線程;

04.Java中sleep和wait方法的區(qū)別?

1.所屬類不同
??1.1 wait() 是Object中的實例方法
??1.2 sleep()是Thread的靜態(tài)方法嘹裂。

2.喚醒機制不同。
??2.1 wait() 沒有設(shè)置最大時間情況下摔握,必須等待對象調(diào)用notify() 或notifyAll()方法寄狼。
??2.2 sleep是到指定時間自動喚醒。

3.鎖機制不同:
??3.1 wait()釋放鎖氨淌,調(diào)用對象的wait()方法導(dǎo)致當(dāng)前線程放棄對象的鎖(線程暫停執(zhí)行)泊愧,進入對象的等待池(wait pool),只有調(diào)用對象的notify()方法(或notifyAll()方法)時才能喚醒等待池中的線程進入等鎖池(lock pool)盛正,如果線程重新獲得對象的鎖就可以進;
??3.2 sleep()只是讓線程休眠删咱,讓出cpu資源,不會釋放鎖,當(dāng)休眠時間結(jié)束后蛮艰,線程會恢復(fù)到就緒狀態(tài)腋腮,但是不會立刻執(zhí)行,可能會有其他優(yōu)先級高的線程搶占資源壤蚜。

4.使用位置不同即寡。
??4.1 wait()必須持有對象鎖,寫在同步方法或者synchronized()語句塊中袜刷。
??4.2 sleep()可以使用在任意地方聪富。

5.異常處理
??5.1 sleep()必須捕獲異常
??5.2 wait(),notify()和notifyAll()不需要捕獲異常著蟹。

  1. 鎖的處理
    ??6.1 調(diào)用wait()方法的線程會釋放持有的對象鎖墩蔓,從而讓其他在此對象上等待的線程有機會獲得該對象鎖;
    ??6.2 sleep()方法在暫停線程時,并不會釋放任何鎖資源萧豆。

05.并發(fā)編程的三大特性?

1)可見性
可見性是指當(dāng)一個線程修改了共享變量后奸披,其他線程能夠立即看見這個修改。

2)原子性
原子性是指一個操作是不可中斷的涮雷,要么全部執(zhí)行成功要么全部執(zhí)行失敗阵面。

3)有序性
有序性是指程序指令按照預(yù)期的順序執(zhí)行而非亂序執(zhí)行,亂序又分為編譯器亂序和CPU執(zhí)行亂序

06 什么是CAS? 有什么優(yōu)缺點?

6.1什么是CAS?
??CAS是compare and swap的縮寫洪鸭,即我們所說的比較交換样刷。CAS是一種基于鎖的操作,而且是樂觀鎖览爵。

6.2 CAS優(yōu)點?
??高效性:CAS能夠在無鎖的情況下進行并發(fā)操作置鼻,避免了線程之間的互斥和阻塞。
??無死鎖:CAS不會引起死鎖蜓竹,因為它不需要加鎖箕母。
??確保操作的原子性:CAS可以確保并發(fā)環(huán)境下對于同一內(nèi)存位置的操作具有原子性储藐,避免數(shù)據(jù)不一致的問題。

6.3 CAS缺點?

  • ABA問題:當(dāng)一個值從A變成B嘶是,再變成A時邑茄,如果CAS檢查的時候只檢查了值是否等于A,那么CAS將認(rèn)為這個值沒有發(fā)生變化俊啼,可能會引發(fā)一些問題。
  • 只能保證一個共享變量的原子操作:如果需要對多個變量進行原子操作左医,CAS就無法滿足需求授帕。
  • 自旋開銷大:在高并發(fā)場景下,如果CAS一致失敗浮梢,就會一直自旋跛十,占用CPU資源,導(dǎo)致性能下降秕硝。
  • 對CPU的緩存使用可能存在問題:由于CAS需要訪問內(nèi)存芥映,所以在高并發(fā)環(huán)境下可能會引發(fā)CPU緩存一致性的問題。

@Contended 注解有什么用?

Java8引入了@Contented這個新的注解來減少偽共享(False Sharing)的發(fā)生远豺。
@sun.misc.Contended注解是被設(shè)計用來解決偽共享問題的;

Java8中@Contended和偽共享
@Contended注解有什么用奈偏?

Java中的四種引用類型?

Java中的四種引用類型分別是強引用(Strong Reference)、軟引用(Soft Reference)躯护、弱引用(Weak Reference)和虛引用(Phantom Reference)惊来。

  • 強引用(Strong Reference):是使用最普遍的引用類型,它直接指向?qū)ο蠊字停⑶抑灰嬖趶娨貌靡希占骶筒粫厥赵搶ο蟆@纾篛bject obj = new Object()继准。

  • 軟引用(Soft Reference):是一種比較靈活的引用類型枉证,當(dāng)堆內(nèi)存不足時,垃圾收集器會優(yōu)先回收軟引用指向的對象移必。一般用于內(nèi)存敏感的程序中室谚,如緩存處理等。例如:SoftReference softRef = new SoftReference(obj)避凝。

  • 弱引用(Weak Reference):是一種比軟引用更弱的引用類型舞萄,它的生命周期只能存活到下一次垃圾收集之前,即只要被垃圾收集器掃描到管削,就會被回收倒脓。例如:WeakReference weakRef = new WeakReference(obj)。

  • 虛引用(Phantom Reference):是一種最弱的引用類型含思,無法通過虛引用訪問對象本身崎弃,僅用于跟蹤對象被垃圾回收的狀態(tài)甘晤。例如:
    ReferenceQueue queue = new ReferenceQueue();
    PhantomReference phantomRef = new PhantomReference(obj,queue)。

ThreadLocal的內(nèi)存泄漏問題?

為什么會內(nèi)存泄漏?
ThreadLocal就相當(dāng)于一個訪問工具類饲做,通過操作ThreadLocal對象的方法 來操作存儲在當(dāng)前線程內(nèi)部的ThreadLocalMap里的值;ThreadLocalMap是一個哈希數(shù)組线婚,key為ThreadLocal對象,Value為一個Object類型的值;調(diào)用ThreadLocal.get() 方法的時候盆均,會將當(dāng)前ThreadLocal對象傳過去塞弊,所以可以獲取到指定ThreadLocal設(shè)置到當(dāng)前線程的值;如果ThreadLocal回收了,則此時就沒有方式能夠訪問到val了,所以val就是不會再被程序用到泪姨,但是由于Thread還存在就無法回收游沿,那么此時便存在了內(nèi)存泄漏!

解決辦法:
1.ThreadLocal被回收后,及時用remove()方法清理ThreadLocal變量值(ThreadLocalMap中的key是Entry,Entry是弱引用,調(diào)用remove()方法會出發(fā)GC,回收已經(jīng)無引用的value);
2.避免使用靜態(tài)ThreadLocal變量;

ThreadLocal內(nèi)存泄漏問題講解

Java中鎖的分類?

Java中鎖分為以下幾種:

  • 樂觀鎖肮砾、悲觀鎖
  • 獨享鎖诀黍、共享鎖
  • 公平鎖、非公平鎖
  • 互斥鎖仗处、讀寫鎖
  • 可重入鎖
  • 分段鎖
  • 鎖升級(無鎖 -> 偏向鎖 -> 輕量級鎖 -> 重量級鎖) JDK1.6
  • 單體鎖眯勾、分布式鎖
    Java鎖的種類

Synchronized在JDK1.6中的優(yōu)化?

jdk1.6對synchronized做了優(yōu)化,分別如下三點:

1 鎖消除:
如果synchronized的內(nèi)容不可能引起同步問題婆誓,則編譯時忽略synchronized吃环,變成沒有鎖的代碼

2 鎖膨脹:
假如在for循環(huán)內(nèi)設(shè)置synchronized代碼塊,每次循環(huán)都會導(dǎo)致加鎖和解鎖洋幻,這會極大的浪費性能模叙,因此jdk1.6以及后面的版本,會將synchronized代碼塊的范圍膨脹到for循環(huán)外

public void test(){
    for (int i=0;i<10;i++){
        synchronized (this){
            count++;
        }
    }
}

3 鎖升級
先是偏向鎖鞋屈,競爭激烈時轉(zhuǎn)成輕量級鎖范咨,如果多次自旋失敗,則升級成重量級鎖;
Synchronized在JDK1.6中的優(yōu)化

Synchronized的實現(xiàn)原理?

synchronized是一種對象鎖厂庇,是可重入的渠啊,但是不可中斷的(這個不可中斷指的是在阻塞隊列中排隊是不可中斷),非公平的鎖。

synchronized是基于JVM內(nèi)置鎖實現(xiàn)权旷,通過內(nèi)部對象Monitor(監(jiān)視器鎖)實現(xiàn)替蛉,基于進入與退出Monitor對象實現(xiàn)方法與代碼塊同步,監(jiān)視器鎖的實現(xiàn)依賴底層操作系統(tǒng)的Mutex lock(互斥鎖)實現(xiàn)拄氯。

加了synchronized后躲查,在字節(jié)碼會有二條指令,如代碼第4和13標(biāo)識位

synchronized (Test3.class){
     System.out.println(1);
}
        4: monitorenter
        5: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        8: iconst_1
        9: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V
       12: aload_1
       13: monitorexit

synchronized原理詳解
深入理解synchronized底層原理,一篇文章就夠了译柏!

什么是AQS?

??AQS镣煮,即AbstractQueuedSynchronizer, 隊列同步器,它是Java并發(fā)用來構(gòu)建鎖和其他同步組件的基礎(chǔ)框架鄙麦。它維護了一個volatile int state(代表共享資源)和一個FIFO(雙向隊列)線程等待隊列(多線程爭用資源被阻塞時會進入此隊列);

??AQS是一個抽象類典唇,主是是以繼承的方式使用镊折。AQS本身是沒有實現(xiàn)任何同步接口的,它僅僅只是定義了同步狀態(tài)的獲取和釋放的方法來供自定義的同步組件的使用介衔。從圖中可以看出恨胚,在java的同步組件中,AQS的子類(Sync等)一般是同步組件的靜態(tài)內(nèi)部類炎咖,即通過組合的方式使用赃泡。

??抽象的隊列式的同步器,AQS定義了一套多線程訪問共享資源的同步器框架乘盼,許多同步類實現(xiàn)都依賴于它急迂,如常用的ReentrantLock/Semaphore/CountDownLatch

image.png

AQS是什么?

AQS喚醒節(jié)點時,為何從后往前找?

1.node 節(jié)點在插入整個 AQS 隊列當(dāng)中時蹦肴,先把當(dāng)前節(jié)點的上一個指針指向前面的節(jié)點,再把 tail 指向自己猴娩;這個時候會有一個 CPU 調(diào)度問題阴幌,如果這個時候我卡在這個位置,那么從前往后找就會造成節(jié)點丟失卷中。
2.cancelAcquire 方法也是先去調(diào)整上一個指針的指向矛双,next 指針后續(xù)才動;
3.所以無論是我們節(jié)點插入的過程還是某一個節(jié)點取消蟆豫,更改指針的過程都是先動上一個指針在動 next 的议忽,所以 prev 這個節(jié)點指向相對來說優(yōu)先級更高,或者時效性更好十减,這樣我們就知道它為什么非要從后往前找了栈幸,因為從前往后極大的可能錯過某一個節(jié)點,從而造成某一個 node 在那邊被掛起了帮辟,但是你之前的線程已經(jīng)釋放鎖資源了速址,并沒有喚醒我造成鎖饑餓的問題。
AQS 喚醒節(jié)點的時候由驹,為什么是從后往前找芍锚?

ReentrantLock和synchronized的區(qū)別?

synchronized 和 ReentrantLock 都是 Java 中提供的可重入鎖,二者的主要區(qū)別有以下 5 個:

1.用法不同:synchronized 可以用來修飾普通方法蔓榄、靜態(tài)方法和代碼塊并炮,而 ReentrantLock 只能用于代碼塊。
2.獲取鎖和釋放鎖的機制不同:synchronized 是自動加鎖和釋放鎖的甥郑,而 ReentrantLock 需要手動加鎖和釋放鎖逃魄。
3.鎖類型不同:synchronized 是非公平鎖,而 ReentrantLock 默認(rèn)為非公平鎖澜搅,也可以手動指定為公平鎖嗅钻。
4.響應(yīng)中斷不同:ReentrantLock 可以響應(yīng)中斷皂冰,解決死鎖的問題,而 synchronized 不能響應(yīng)中斷养篓。
5.底層實現(xiàn)不同:synchronized 是 JVM 層面通過監(jiān)視器實現(xiàn)的秃流,而 ReentrantLock 是基于 AQS 實現(xiàn)的。
synchronized和ReentrantLock有什么區(qū)別柳弄?

ReentrantReadWriteLock的實現(xiàn)原理?

簡單來說舶胀,就是讀鎖可以共享,但是寫鎖必須獨占;

讀寫鎖用的是同一個Sync同步器碧注,所以有相同的阻塞隊列和state

JDK中提供了哪些線程池?

1.newFixedThreadPool:定長線程池;
可控制線程最大并發(fā)數(shù)嚣伐,超出的線程會在隊列中等待

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

2.newSingThreadPool:單例線程池
它只會用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行萍丐。

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory));
}

3.newCachedThreadPool:緩存線程池;
如果線程池長度超過處理需要轩端,可靈活回收空閑線程,若無可回收逝变,則新建線程基茵。

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

4.newScheduleThreadpool:定時線程池
支持定時及周期性任務(wù)執(zhí)行

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

5.newWorkStealingPool(工作竊取線程池):JDK 8引入,內(nèi)部構(gòu)建一個ForkJoinPool壳影,創(chuàng)建持有足夠線程來支持給定的并行度的線程池拱层。該線程池使用多個隊列宴咧,每個線程維護一個自己的隊列根灯。當(dāng)一個線程完成自己隊列中的任務(wù)后,會從其他線程的隊列中竊取任務(wù)執(zhí)行掺栅,因此構(gòu)造方法中把CPU數(shù)量設(shè)置為默認(rèn)的并行度烙肺。

線程池的核心參數(shù)有什么?

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

1.核心線程數(shù)(Core Pool Size):線程池中最小的線程數(shù),即在線程池中一直保持的線程數(shù)量氧卧,不受空閑時間的影響;
2.最大線程數(shù)(最大池大小);
3.空閑線程存活時間(Keep Alive Time):當(dāng)線程池中的線程數(shù)超過核心線程數(shù)時茬高,多余的線程會被回收,此參數(shù)即為非核心線程的空閑時間假抄,超過此時間將被回收;
4.工作隊列(Work Queue):用于存儲等待執(zhí)行的任務(wù)的隊列怎栽,當(dāng)線程池中的線程數(shù)達到核心線程數(shù)時,新的任務(wù)將被加入工作隊列等待執(zhí)行;
5.拒絕策略(Reject Execution Handler):當(dāng)線程池和工作隊列都已經(jīng)達到最大容量宿饱,無法再接收新的任務(wù)時熏瞄,拒絕策略將被觸發(fā)。常見的拒絕策略有拋出異常谬以、直接丟棄任務(wù)强饮、丟棄隊列中最老的任務(wù)等。
6.線程工廠 (Thread Factory):用于創(chuàng)建新的線程为黎,可定制線程名字邮丰、線程組行您、優(yōu)先級等。
7.時間單位(TimeUnit):剪廉。

線程池的拒絕策略?

  • AbortPolicy(默認(rèn)):丟棄任務(wù)并拋出 RejectedExecutionException 異常娃循。
  • CallerRunsPolicy:由調(diào)用線程處理該任務(wù)。
  • DiscardPolicy:丟棄任務(wù)斗蒋,但是不拋出異常捌斧。可以配合這種模式進行自定義的處理方式泉沾。
  • DiscardOldestPolicy:丟棄隊列最早的未處理任務(wù)捞蚂,然后重新嘗試執(zhí)行任務(wù)。

線程池的狀態(tài)?

  • RUNNING :能接受新提交的任務(wù)跷究,并且也能處理阻塞隊列中的任務(wù)姓迅。
  • SHUTDOWN:關(guān)閉狀態(tài),不再接受新提交的任務(wù)俊马,但卻可以繼續(xù)處理阻塞隊列中已保存的任務(wù)丁存。在線程池處于 RUNNING 狀態(tài)時,調(diào)用 shutdown() 方法會使線程池進入到該狀態(tài)潭袱。(finalize() 方法在執(zhí)行過程中也會調(diào)用 shutdown() 方法進入該狀態(tài))。
  • STOP:不能接受新任務(wù)锋恬,也不處理隊列中的任務(wù)屯换,會中斷正在處理任務(wù)的線程。在線程池處于 RUNNING 或 SHUTDOWN 狀態(tài)時与学,調(diào)用 shutdownNow() 方法會使線程池進入到該狀態(tài)彤悔。
  • TIDYING:如果所有的任務(wù)都已終止了,workerCount (有效線程數(shù)) 為0索守,線程池進入該狀態(tài)后會調(diào)用 terminated() 方法進入 TERMINATED 狀態(tài)晕窑。
  • TERMINATED:在 terminated() 方法執(zhí)行完后進入該狀態(tài),默認(rèn) terminated() 方法中什么也沒有做卵佛。


    image.png

線程池的執(zhí)行流程?

  1. 提交一個新線程任務(wù)杨赤,線程池會在線程池中分配一個空閑線程,用于執(zhí)行線程任務(wù)截汪;
  2. 如果線程池中不存在空閑線程疾牲,則線程池會判斷當(dāng)前“存活的線程數(shù)”是否小于核心線程數(shù)corePoolSize。
  • 如果小于核心線程數(shù)corePoolSize衙解,線程池會創(chuàng)建一個新線程(核心線程)去處理新線程任務(wù)阳柔;
  • 如果大于核心線程數(shù)corePoolSize,線程池會檢查工作隊列蚓峦;
  • 如果工作隊列未滿舌剂,則將該線程任務(wù)放入工作隊列進行等待济锄。線程池中如果出現(xiàn)空閑線程,將從工作隊列中按照FIFO的規(guī)則取出1個線程任務(wù)并分配執(zhí)行霍转;
  • 如果工作隊列已滿荐绝,則判斷線程數(shù)是否達到最大線程數(shù)maximumPoolSize;
  • 如果當(dāng)前“存活線程數(shù)”沒有達到最大線程數(shù)maximumPoolSize谴忧,則創(chuàng)建一個新線程(非核心線程)執(zhí)行新線程任務(wù)很泊;

線程池為什么要shutdown()?

1.線程池在處理任務(wù)的時候,內(nèi)部會啟動線程沾谓,而線程的啟動在addWorker中委造,也是使用Thread對象的start()方法。這種線程會占用一個虛擬機棧均驶,在JVM層面輸入GC root昏兆,根據(jù)可達性分析算法,這個線程就不會被回收妇穴,會一直占用JVM內(nèi)存資源爬虱,這樣就會造成所有的線程的核心線程永遠都不會被回收,也就是內(nèi)存泄漏腾它。
2.另外跑筝,當(dāng)執(zhí)行任務(wù)時,start()會調(diào)用Worker的run方法(實現(xiàn)了Runnable),而在Worker的run方法中又使用了ThreadPoolExecutor的runWorker(Worker w)方法瞒滴。也就是啟動了的線程曲梗,還指向了Worker對象。Worker對象也無法被回收妓忍。
3.同時Worker是ThreadPoolExecutor的內(nèi)部類虏两,如果內(nèi)部類不能被回收,外部類ThreadPoolExecutor也不能被回收世剖。所以說定罢,如果沒有關(guān)閉ThreadPoolExecutor,就會造成堆內(nèi)存占用很嚴(yán)重旁瘫,進而影響GC祖凫。

線程池為什么要shutdown()

線程池添加工作線程的流程?

線程池為何要構(gòu)建空任務(wù)的非核心線程?

線程池使用完畢為何必須shutdown()?

1.釋放資源:線程池內(nèi)部會創(chuàng)建一定數(shù)量的線程以及其他相關(guān)資源,如線程隊列酬凳、線程池管理器等蝙场。如果不及時關(guān)閉線程池,這些資源將一直占用系統(tǒng)資源粱年,可能導(dǎo)致內(nèi)存泄漏或資源浪費售滤。
2.防止任務(wù)丟失:線程池中可能還有未執(zhí)行的任務(wù),如果不關(guān)閉線程池,這些任務(wù)將無法得到執(zhí)行完箩。關(guān)閉線程池時赐俗,會等待所有已提交的任務(wù)執(zhí)行完畢,確保任務(wù)不會丟失弊知。
3.優(yōu)雅終止:關(guān)閉線程池可以讓線程池中的線程正常執(zhí)行完當(dāng)前任務(wù)后停止阻逮,避免突然終止線程導(dǎo)致的資源釋放不完整或狀態(tài)不一致的問題。
4.避免程序阻塞:在某些情況下秩彤,如果不關(guān)閉線程池叔扼,程序可能會一直等待線程池中的任務(wù)執(zhí)行完畢,從而導(dǎo)致程序阻塞漫雷,無法繼續(xù)執(zhí)行后續(xù)的邏輯瓜富。

線程池的核心參數(shù)到底如何設(shè)置?

配置線程數(shù)量之前,首先要看任務(wù)的類型是 IO密集型降盹,還是CPU密集型与柑?

IO密集型:頻繁讀取磁盤上的數(shù)據(jù),或者需要通過網(wǎng)絡(luò)遠程調(diào)用接口蓄坏。

CPU密集型:非常復(fù)雜的調(diào)用价捧,循環(huán)次數(shù)很多,或者遞歸調(diào)用層次很深等涡戳。

  • IO密集型配置線程數(shù)經(jīng)驗值是:2N结蟋,其中N代表CPU核數(shù)。

  • CPU密集型配置線程數(shù)經(jīng)驗值是:N + 1渔彰,其中N代表CPU核數(shù)嵌屎。

ConcurrentHashMap在1.8做了什么優(yōu)化?

JDK1.8放棄了鎖分段的做法,采用CAS和synchronized方式處理并發(fā)胳岂。以put操作為例编整,CAS方式確定key的數(shù)組下標(biāo)舔稀,synchronized保證鏈表節(jié)點的同步效果乳丰。
jdk1.8ConcurrentHashMap是數(shù)組+鏈表,或者數(shù)組+紅黑樹結(jié)構(gòu),并發(fā)控制使用Synchronized關(guān)鍵字和CAS操作内贮。

ConcurrentHashMap的散列算法?
ConcurrentHashMap初始化數(shù)組的流程?
ConcurrentHashMap擴容的流程?
ConcurrentHashMap讀取數(shù)據(jù)的流程?
ConcurrentHashMap中的計數(shù)器的實現(xiàn)?

ConcurrentHashMap擴容會阻塞進行put嗎产园?

1.在ConcurrentHashMap擴容時,不會阻塞正在進行put操作的線程夜郁。當(dāng)需要擴容時什燕,ConcurrentHashMap會創(chuàng)建一個新的數(shù)組,并將舊的數(shù)組中的元素重新分配到新的數(shù)組中竞端。這個過程中屎即,新的數(shù)組會被分成一定數(shù)量的段,每個段由一個線程負(fù)責(zé)處理。當(dāng)一個線程完成了它分配到的段的搬遷任務(wù)后技俐,它會將這個段標(biāo)記為已完成乘陪,并開始處理下一個未完成的段。因此雕擂,即使有線程正在put元素啡邑,也不會阻塞擴容任務(wù)的進行。
2.當(dāng)ConcurrentHashMap中的元素數(shù)量達到了擴容閾值時井赌,才會開始擴容操作谤逼。默認(rèn)情況下,擴容閾值是當(dāng)前容量的0.75仇穗。在擴容過程中流部,如果有新的元素需要插入,則會先將元素插入到舊的數(shù)組中仪缸。只有當(dāng)擴容完成后贵涵,才會將舊的數(shù)組中的元素重新分配到新的數(shù)組中。因此恰画,在極端情況下宾茂,如果大量元素都先被放入了舊的數(shù)組中,可能會導(dǎo)致擴容操作比較耗時拴还,但不會影響新元素的插入跨晴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市片林,隨后出現(xiàn)的幾起案子端盆,更是在濱河造成了極大的恐慌,老刑警劉巖费封,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焕妙,死亡現(xiàn)場離奇詭異,居然都是意外死亡弓摘,警方通過查閱死者的電腦和手機焚鹊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來韧献,“玉大人末患,你說我怎么就攤上這事〈敢ぃ” “怎么了璧针?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渊啰。 經(jīng)常有香客問我探橱,道長申屹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任隧膏,我火速辦了婚禮独柑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘私植。我一直安慰自己忌栅,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布曲稼。 她就那樣靜靜地躺著索绪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贫悄。 梳的紋絲不亂的頭發(fā)上瑞驱,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音窄坦,去河邊找鬼唤反。 笑死,一個胖子當(dāng)著我的面吹牛鸭津,可吹牛的內(nèi)容都是我干的彤侍。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼逆趋,長吁一口氣:“原來是場噩夢啊……” “哼盏阶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起闻书,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤名斟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后魄眉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砰盐,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年坑律,在試婚紗的時候發(fā)現(xiàn)自己被綠了岩梳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡脾歇,死狀恐怖蒋腮,靈堂內(nèi)的尸體忽然破棺而出淘捡,到底是詐尸還是另有隱情藕各,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布焦除,位于F島的核電站激况,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乌逐,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一竭讳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浙踢,春花似錦绢慢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蹬挤,卻和暖如春缚窿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背焰扳。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工倦零, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吨悍。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓扫茅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親育瓜。 傳聞我的和親對象是個殘疾皇子诞帐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容