Java併發編程 - 多線程/併發面試題集合(持續更新)

1. 如今有線程T一、T2和T3。你如何確保T2線程在T1以後執行,而且T3線程在T2以後執行。

http://www.javashuo.com/article/p-uamljweq-dz.htmlhtml

 

2. Java 中新的Lock接口相對於同步代碼塊(synchronized block)有什麼優點?若是讓你實現一個高性能緩存,支持併發讀取和單一寫入,你如何保證數據完整性。

Lock接口的最大優點是它爲讀和寫提供兩個單獨的鎖(ReentrantReadWriteLock),ReentrantReadWriteLock的特色是:「讀讀共享」,「讀寫互斥」,「寫寫互斥」。(附:Lock取款機示例java

高性能緩存簡易示例:ios

public class ReadWriteMap {

    private final Map<Object, Object> map;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    public ReadWriteMap(Map<Object, Object> map) {
        this.map = map;
    }

    public Object put(Object key, Object value) {
        try {
            writeLock.lock();
            return map.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }

    public Object get(Object key) {
        try {
            readLock.lock();
            return map.get(key);
        } finally {
            writeLock.unlock();
        }
    }
}

 

3.Java中wait和sleep方法有什麼區別。

sleep() 是Thread類的靜態方法,調用此方法會讓當前線程暫停執行指定的時間,將執行機會(CPU)讓給其餘線程,可是對象的鎖(或監視器)依然保持。休眠時間結束後線程自動回到就緒狀態。編程

wait() 是Object類的方法,調用此方法會讓當前線程暫停執行指定的時間,並放棄對象鎖進入等待池(wait pool)。只有調用對象的notify()或notifyAll()方法才能喚醒等待池中的線程進入等鎖池(lockpool)。若是線程從新得到對象的鎖就能夠進入就緒狀態。緩存

wait()方法多用於線程間通訊,而sleep()只是在執行時暫停。安全

 

4.如何在Java中實現一個阻塞隊列。

阻塞隊列(BlockingQueue)是一個支持兩個附加操做的隊列。這兩個附加的操做支持阻塞的插入和移除方法。
1)阻塞的插入:當隊列滿時,隊列會阻塞插入元素的線程,直到隊列不滿。
2)阻塞的移除:當隊列空時,隊列會阻塞移除元素的線程,直到隊列不空。多線程

public class BlockingQueue {

    private final List<Object> queue = new LinkedList<>();
    private int capacity = 10;

    public BlockingQueue() {
    }

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public synchronized Object put(Object item) throws InterruptedException {
        while (queue.size() >= capacity) {
            wait();
        }
        queue.add(item);
        notifyAll();
        return item;
    }

    public synchronized void remove() throws InterruptedException {
        while (0 == queue.size()) {
            wait();
        }
        queue.remove(0);
        notifyAll();
    }

    public synchronized int getSize() {
        return queue.size();
    }
}

附:我在實現阻塞隊列時遇到的一個問題 http://www.javashuo.com/article/p-qlcubcau-eb.html併發

 

5.寫一段死鎖代碼。說說你在Java中如何解決死鎖。

http://www.javashuo.com/article/p-hcxhfmup-eg.html異步

 

6.Java中volatile關鍵字是什麼。你如何使用它。它和Java中的同步方法有什麼區別。

http://www.javashuo.com/article/p-aohsrijj-dd.html性能

 

7.既然start()方法會調用run()方法,爲何咱們調用 start() 方法,而不直接調用 run() 方法。

調用start()方法時,它會新建一個線程而後執行run()方法中的代碼。

直接調用run()方法,則不會建立新線程,方法中的代碼會在當前調用者的線程中執行。驗證以下:

    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> System.out.println(Thread.currentThread().getName()));
        Thread thread2 = new Thread(() -> System.out.println(Thread.currentThread().getName()));

        thread1.setName("thread1");
        thread1.start();

        Thread.sleep(1000);

        thread2.setName("thread2");
        thread2.run();
    }

 

8.什麼是線程調度,Java中使用了什麼線程調度方法。

線程調度是指系統爲線程分配處理器使用權的過程,主要調度方式分兩種,分別是協同式線程調度和搶佔式線程調度。

  • 協同式調度。線程執行時間由線程自己來控制,線程把本身的工做執行完以後,要主動通知系統切換到另一個線程上。最大好處是實現簡單,且切換操做對線程本身是可知的,沒啥線程同步問題。壞處是線程執行時間不可控制,若是一個線程有問題,可能一直阻塞在那裏。
  • 搶佔式調度。每一個線程將由系統來分配執行時間,線程的切換不禁線程自己來決定(Java中,Thread.yield()可讓出執行時間,但沒法獲取執行時間)。線程執行時間系統可控,也不會有一個線程致使整個進程阻塞。

Java線程調度就是搶佔式調度。

 

9.Java實現多線程的方式。

1)繼承Thread類:看jdk源碼能夠發現,Thread類實際上是實現了Runnable接口的一個實例,繼承Thread類後須要重寫run方法並經過start方法啓動線程。繼承Thread類耦合性太強了,由於Java只能單繼承,因此不利於擴展。

2)實現Runnable接口:經過實現Runnable接口並重寫run方法,並把Runnable實例傳給Thread對象,Thread的start方法調用run方法再經過調用Runnable實例的run方法啓動線程。因此若是一個類繼承了另一個父類,此時要實現多線程就不能經過繼承Thread的類實現。

3)實現Callable接口:經過實現Callable接口並重寫call方法,並把Callable實例傳給FutureTask對象,再把FutureTask對象傳給Thread對象。它與Thread、Runnable最大的不一樣是Callable能返回一個異步處理的結果Future對象並能拋出異常,而其餘兩種不能。

 

10.Runnable接口和Callable接口的區別。

Runnable接口中的run()方法的返回值是void,它只是純粹地去執行run()方法中的代碼而已。

Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合能夠用來獲取異步執行的結果。

 

11.多線程有什麼做用。

1)發揮多核CPU的優點

若是是單線程的程序,那麼在雙核CPU上就浪費了50%,在4核CPU上就浪費了75%。單核CPU上的"多線程"是假的多線程,同一時間處理器只會處理一段邏輯,只不過線程之間切換得比較快,看着像多個線程"同時"運行罷了。多核CPU上的多線程纔是真正的多線程,它能讓你的多段邏輯同時工做,多線程,能夠真正發揮出多核CPU的優點來,達到充分利用CPU的目的。

2)防止阻塞

從程序運行效率的角度來看,單核CPU不但不會發揮出多線程的優點,反而會由於在單核CPU上運行多線程致使線程上下文的切換,而下降程序總體的效率。可是單核CPU咱們仍是要應用多線程,就是爲了防止阻塞。試想,若是單核CPU使用單線程,那麼只要這個線程阻塞了,比方說遠程讀取某個數據,對端遲遲未返回又沒有設置超時時間,那麼整個程序在數據返回來以前就中止運行了。多線程能夠防止這個問題,多條線程同時運行,哪怕一條線程的代碼執行讀取數據阻塞,也不會影響其它任務的執行。

3)便於建模

這是另一個沒有這麼明顯的優勢了。假設有一個大的任務A,單線程編程,那麼就要考慮不少,創建整個程序模型比較麻煩。可是若是把這個大的任務A分解成幾個小任務,任務B、任務C、任務D,分別創建程序模型,並經過多線程分別運行這幾個任務,那就簡單不少了。

 

12.CyclicBarrier和CountDownLatch的區別

兩個看上去有點像的類,都在java.util.concurrent下,均可以用來表示代碼運行到某個點上,兩者的區別在於:

1)CyclicBarrier的某個線程運行到某個點上以後,該線程即中止運行,直到全部的線程都到達了這個點,全部線程才從新運行;CountDownLatch則不是,某線程運行到某個點上以後,只是給某個數值-1而已,該線程繼續運行。

2)CyclicBarrier只能喚起一個任務,CountDownLatch能夠喚起多個任務。

3) CyclicBarrier可重用,CountDownLatch不可重用,計數值爲0該CountDownLatch就不可再用了。

 

13.什麼是線程安全。

若是你的代碼在多線程下執行和在單線程下執行永遠都能得到同樣的結果,那麼你的代碼就是線程安全的。

這個問題有值得一提的地方,就是線程安全也是有幾個級別的:

1)不可變

像String、Integer、Long這些,都是final類型的類,任何一個線程都改變不了它們的值,要改變除非新建立一個,所以這些不可變對象不須要任何同步手段就能夠直接在多線程環境下使用。

2)絕對線程安全

無論運行時環境如何,調用者都不須要額外的同步措施。要作到這一點一般須要付出許多額外的代價,Java中標註本身是線程安全的類,實際上絕大多數都不是線程安全的,不過絕對線程安全的類,Java中也有,比方說CopyOnWriteArrayList、CopyOnWriteArraySet。

3)相對線程安全

相對線程安全也就是咱們一般意義上所說的線程安全,像Vector這種,add、remove方法都是原子操做,不會被打斷,但也僅限於此,若是有個線程在遍歷某個Vector、有個線程同時在add這個Vector,99%的狀況下都會出現ConcurrentModificationException,也就是fail-fast機制

4)線程非安全

這個就沒什麼好說的了,ArrayList、LinkedList、HashMap等都是線程非安全的類。

 

14.Java如何獲取線程的dump文件。

死循環、死鎖、阻塞、頁面打開慢等問題,打線程dump是最好的解決問題的途徑。所謂線程dump也就是線程堆棧,獲取到線程堆棧有兩步:

1)獲取到線程的pid,能夠經過使用jps命令,在Linux環境下還可使用ps -ef | grep java

2)打印線程堆棧,能夠經過使用jstack pid命令,在Linux環境下還可使用kill -3 pid

另外提一點,Thread類提供了一個getStackTrace()方法也能夠用於獲取線程堆棧。這是一個實例方法,所以此方法是和具體線程實例綁定的,每次獲取獲取到的是具體某個線程當前運行的堆棧。

 

15.一個線程若是出現了運行時異常會怎麼樣。

若是這個異常沒有被捕獲的話,這個線程就中止執行了。另外重要的一點是:若是這個線程持有某個對象的監視器,那麼這個對象監視器會被當即釋放。

 

16.如何在兩個線程之間共享數據。

經過在線程之間共享對象就能夠了,而後經過wait/notify/notifyAll、await/signal/signalAll進行喚起和等待,比方說阻塞隊列BlockingQueue就是爲線程之間共享數據而設計的。

 

17.生產者消費者模型的做用是什麼

1)經過平衡生產者的生產能力和消費者的消費能力來提高整個系統的運行效率,這是生產者消費者模型最重要的做用。

2)解耦,這是生產者消費者模型附帶的做用,解耦意味着生產者和消費者之間的聯繫少,聯繫越少越能夠獨自發展而不須要收到相互的制約。

相關文章
相關標籤/搜索