1. 程式人生 > >Java多執行緒常用面試題(含答案,精心總結整理)

Java多執行緒常用面試題(含答案,精心總結整理)

Java併發程式設計問題是面試過程中很容易遇到的問題,提前準備是解決問題的最好辦法,將試題總結起來,時常檢視會有奇效。

現在有T1、T2、T3三個執行緒,你怎樣保證T2在T1執行完後執行,T3在T2執行完後執行?

這個執行緒問題通常會在第一輪或電話面試階段被問到,目的是檢測你對”join”方法是否熟悉。這個多執行緒問題比較簡單,可以用join方法實現。

核心:

thread.Join把指定的執行緒加入到當前執行緒,可以將兩個交替執行的執行緒合併為順序執行的執行緒。 
比如線上程B中呼叫了執行緒A的Join()方法,直到執行緒A執行完畢後,才會繼續執行執行緒B。 
想要更深入瞭解,建議看一下join的原始碼,也很簡單的,使用wait方法實現的。

t.join(); //呼叫join方法,等待執行緒t執行完畢 
t.join(1000); //等待 t 執行緒,等待時間是1000毫秒。

程式碼實現:

public static void main(String[] args) {
        method01();
        method02();
    }

    /**
     * 第一種實現方式,順序寫死線上程程式碼的內部了,有時候不方便
     */
    private static void method01() {
        Thread t1 = new Thread(new Runnable() {
            @Override
public void run() { System.out.println("t1 is finished"); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t2 is finished"
); } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { try { t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t3 is finished"); } }); t3.start(); t2.start(); t1.start(); } /** * 第二種實現方式,執行緒執行順序可以在方法中調換 */ private static void method02(){ Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "執行完成"); } }; Thread t1 = new Thread(runnable, "t1"); Thread t2 = new Thread(runnable, "t2"); Thread t3 = new Thread(runnable, "t3"); try { t1.start(); t1.join(); t2.start(); t2.join(); t3.start(); t3.join(); } catch (InterruptedException e) { e.printStackTrace(); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

在Java中Lock介面比synchronized塊的優勢是什麼?你需要實現一個高效的快取,它允許多個使用者讀,但只允許一個使用者寫,以此來保持它的完整性,你會怎樣去實現它?

這個題的原答案我認為不是很全面。 
Lock介面 和 ReadWriteLock介面 如下:

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

整體上來說Lock是synchronized的擴充套件版,Lock提供了無條件的、可輪詢的(tryLock方法)、定時的(tryLock帶參方法)、可中斷的(lockInterruptibly)、可多條件佇列的(newCondition方法)鎖操作。另外Lock的實現類基本都支援非公平鎖(預設)和公平鎖,synchronized只支援非公平鎖,當然,在大部分情況下,非公平鎖是高效的選擇。

ReadWriteLock是對Lock的運用,具體的實現類是 ReentrantReadWriteLock ,下面用這個類來實現讀寫型別的高效快取:

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 用ReadWriteLock讀寫鎖來實現一個高效的Map快取
 * Created by LEO on 2017/10/30.
 */
public class ReaderAndWriter<K, V> {
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();
    private final Map<K, V> map;

    public ReaderAndWriter(Map<K, V> map) {
        this.map = map;
    }

    /*************   這是用lock()方法寫的   ********************/
//    public V put(K key, V value){
//        writeLock.lock();
//        try {
//            return map.put(key, value);
//        }finally {
//            writeLock.unlock();
//        }
//    }
//    public V get(K key){
//        readLock.lock();
//        try {
//            return map.get(key);
//        }finally {
//            readLock.unlock();
//        }
//    }

    /*************   這是用tryLock()方法寫的   ********************/
    public V put(K key, V value){
        while (true){
            if(writeLock.tryLock()){
                try {
                    System.out.println("put "+ key +" = " + value);
                    return map.put(key, value);
                }finally {
                    writeLock.unlock();
                }
            }
        }
    }
    public V get(K key){
        while (true){
            if (readLock.tryLock()) {
                try {
                    V v = map.get(key);
                    System.out.println("get "+ key +" = " + v);
                    return v;
                } finally {
                    readLock.unlock();
                }
            }
        }
    }


    /********************    下面是測試區       *********************************/
    public static void main(String[] args) {
        final ReaderAndWriter<String, Integer> rw = new ReaderAndWriter<>(new HashMap<>());
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            exec.execute(new TestRunnable(rw));
        }
        exec.shutdown();
    }

    static class TestRunnable implements Runnable{
        private final ReaderAndWriter<String, Integer> rw;
        private final String KEY = "x";

        TestRunnable(ReaderAndWriter<String, Integer> rw) {
            this.rw = rw;
        }

        @Override
        public void run() {
            Random random = new Random();
            int r = random.nextInt(100);
            //生成隨機數,小於30的寫入快取,大於等於30則讀取數字
            if (r < 30){
                rw.put(KEY, r);
            } else {
                rw.get(KEY);
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101

在java中wait和sleep方法的不同?

通常會在電話面試中經常被問到的Java執行緒面試問題。 
最大的不同是在等待時wait會釋放鎖,而sleep一直持有鎖。Wait通常被用於執行緒間互動,sleep通常被用於暫停執行。

此處我想理一下Java多執行緒的基礎知識: 
- Java的多執行緒鎖是掛在物件上的,並不是在方法上的。即每個物件都有一個鎖,當遇到類似synchronized的同步需要時,就會監視(monitor)每個想使用本物件的執行緒按照一定的規則來訪問,規則也就是在同一時間內只能有一個執行緒能訪問此物件。 
- Java中獲取鎖的單位是執行緒。當執行緒A獲取了物件B的鎖,也就是物件B的持有標記上寫的是執行緒A的唯一標識,在需要同步的情況下的話,只有執行緒A能訪問物件B。 
- Thread常用方法有:start/stop/yield/sleep/interrupt/join等,他們是執行緒級別的方法,所以並不會太關心鎖的具體邏輯。 
- Object的執行緒有關方法是:wait/wait(事件引數)/notify/notifyAll,他們是物件的方法,所以使用的時候就有點憋屈了,必須當前執行緒獲取了本物件的鎖才能使用,否則會報異常。但他們能更細粒度的控制鎖,可以釋放鎖。

用Java實現阻塞佇列。

這是一個相對艱難的多執行緒面試問題,它能達到很多的目的。第一,它可以檢測侯選者是否能實際的用Java執行緒寫程式;第二,可以檢測侯選者對併發場景的理解,並且你可以根據這個問很多問題。如果他用wait()和notify()方法來實現阻塞佇列,你可以要求他用最新的Java 5中的併發類來再寫一次。

下面是實現了阻塞的take和put方法的阻塞佇列(分別用synchronized 和 wait/notify 實現):

import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 實現了阻塞的take和put方法的阻塞佇列
 *      分別用synchronized 和 wait/notify 實現
 * @author xuexiaolei
 * @version 2017年11月01日
 */
public class MyBlocingQueue<E> {
    private final List list;
    private final int limit;//有大小限制的

    public MyBlocingQueue(int limit) {
        this.limit = limit;
        this.list = new LinkedList<E>();
    }

    //這是用synchronized寫的,在list空或者滿的時候效率會低,因為會一直輪詢
//    public void put(E e){
//        while(true){
//            synchronized (list){
//                if (list.size() < limit) {
//                    System.out.println("list : " + list.toString());
//                    System.out.println("put : " + e);
//                    list.add(e);
//                    return;
//                }
//            }
//        }
//    }
//    public E take(){
//        while (true) {
//            synchronized (list) {
//                if (list.size() > 0){
//                    System.out.println("list : " + list.toString());
//                    E remove = (E) list.remove(0);
//                    System.out.println("take : " + remove);
//                    return remove;
//                }
//            }
//        }
//    }

    //用wait,notify寫的,在list空或者滿的時候效率會高一點,因為wait釋放鎖,然後等待喚醒
    public synchronized void put(E e){
        while (list.size() == limit){
            try {
                wait();
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
        }
        System.out.println("list : " + list.toString());
        System.out.println("put : " + e);
        list.add(e);
        notifyAll();
    }
    public synchronized E take() {
        while (list.size() == 0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("list : " + list.toString());
        E remove = (E) list.remove(0);
        System.out.println("take : " + remove);
        notifyAll();
        return remove;
    }


    /********************    下面是測試區       *********************************/
    public static void main(String[] args) {
        final MyBlocingQueue<Integer> myBlocingQueue = new MyBlocingQueue(10);
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            exec.execute(new TestRunnable(myBlocingQueue));
        }
        exec.shutdown();
    }

    static class TestRunnable implements Runnable{
        private final MyBlocingQueue<Integer> myBlocingQueue;

        TestRunnable(MyBlocingQueue<Integer> myBlocingQueue) {
            this.myBlocingQueue = myBlocingQueue;
        }

        @Override
        public void run() {
            Random random = new Random();
            int r = random.nextInt(100);
            //生成隨機數,按照一定比率讀取或者放入,可以更改!!!
            if (r < 30){
                myBlocingQueue.put(r);
            } else {
                myBlocingQueue.take();
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 相關推薦

    Java執行常用試題(答案,精心總結整理)

    Java併發程式設計問題是面試過程中很容易遇到的問題,提前準備是解決問題的最好辦法,將試題總結起來,時常檢視會有奇效。 現在有T1、T2、T3三個執行緒,你怎樣保證T2在T1執行完後執行,T3在T2執行完後執行? 這個執行緒問題通常會在第一輪或電話面試階段

    Java執行精選試題

    1、多執行緒有什麼用? 1)發揮多核CPU的優勢 隨著工業的進步,現在的筆記本、桌上型電腦乃至商用的應用伺服器至少也都是雙核的,4核、8核甚至16核的也都不少見,如果是單執行緒的程式,那麼在雙核CPU上就浪費了50%,在4核CPU上就浪費了75%。單核CPU上所謂的"多執行緒"那是假的多

    執行常用試題

    一 面試中關於 synchronized 關鍵字的 5 連擊 1.1 說一說自己對於 synchronized 關鍵字的瞭解  synchronized關鍵字解決的是多個執行緒之間訪問資源的同步性,synchronized關鍵字可以保證被它修飾的方法或者程式碼塊在任意時刻只能有

    執行經典試題答案

    Java實現執行緒有哪幾種方式? 1、繼承Thread類實現多執行緒 2、實現Runnable介面方式實現多執行緒 3、使用ExecutorService、Callable、Future實現有返回結果的多執行緒       多執行緒同步有哪幾種方法?

    試題之——執行併發試題

    1) 什麼是執行緒?   執行緒是作業系統能夠進行運算排程的最小單位,它被包含在程序之中,是程序中的實際運作單位。程式設計師可以通過它進行多處理器程式設計,你可以使用多執行緒對運算密集型任務提速。比如,如果一個執行緒完成一個任務要100毫秒,那麼用十個執行緒完成改任務只需10毫秒。Java在語言層面對多執行

    java執行試題

    面試官:執行緒池有哪些?分別的作用是什麼? 常用的執行緒池有: newSingleThreadExecutor newFixedThreadExecutor newCacheThreadExecutor newScheduleThreadExecutor 1、newSingleTh

    Java執行常用物件

    一、CountDownLatch public class CountDownLatchUtils { private static final int count = 5; public static void main(String[] args) {

    秒殺執行第一篇 執行筆試試題彙總

        系列前言    本系列是本人蔘加微軟亞洲研究院,騰訊研究院,迅雷面試時整理的,另外也加入一些其它IT公司如百度,阿里巴巴的筆試面試題目,因此具有很強的針對性。系列中不但會詳細講解多執行緒同步互斥的各種“招式”,而且會進一步的講解多執行緒同步互斥的“內功心法”。有了“招

    java執行常用建立方式

    1 繼承Thread類重寫run方法 class Thread111 extends Thread{ @Override public void run() { System.out.println("thread1"); } } public class Thread1 {

    Java面試執行相關試題

    1) 什麼是執行緒? 執行緒是作業系統能夠進行運算排程的最小單位,它被包含在程序之中,是程序中的實際運作單位。程式設計師可以通過它進行多處理器程式設計,你可以使用多執行緒對運算密集型任務提速。比如,如果一個執行緒完成一個任務要100毫秒,那麼用十個執行緒完成改任務只需10毫秒。Java在

    Java 執行常用操作方法

    1.多執行緒常用方法:    currentThread() :  獲取當前執行的執行緒    getName() : 獲取執行緒名稱    setName() : 設定執行緒名稱    sleep(long millis) :  是一個靜態方法,使當前執行執行緒進入睡眠狀態

    個人珍藏的80道執行併發試題(1-10答案解析)

    前言 個人珍藏的80道Java多執行緒/併發經典面試題,因為篇幅太長,現在先給出1-10的答案解析哈,後面一起完善,並且上傳github哈~ ❝ https://github.com/whx123/JavaHome ❞ 「公眾號:撿田螺的小男孩」 1. synchronized的實現原理以及鎖優化? sync

    個人珍藏的80道執行併發試題(11-20答案解析)

    ## 前言 個人珍藏的80道Java多執行緒/併發經典面試題,現在給出11-20的答案解析哈,並且上傳github哈~ > https://github.com/whx123/JavaHome [個人珍藏的80道多執行緒併發面試題(1-10答案解析)](https://juejin.im/post/

    Java執行核心技術(一):基礎知識總結

    概念 程序:程序是作業系統結構的基礎,是一次程式的執行,是一個程式及其資料在處理機上順序執行時所發生的活動,是程式在一個程式集合上執行的過程,它是系統進行資源分配和排程的一個基本單位。 執行緒:執行緒是程序中獨立執行的子任務。使用多執行緒技術後,可以在同一時間執行更多不同種

    java執行解說【拾玖】_ThreadLocal總結

    突然發現之前總結的知識沒有覆蓋到ThreadLocal,這裡續上一文是為補充。 首先說說什麼是ThreadLocal。看名字感覺像是本地執行緒之意,其實不然。ThreadLocal其實是Threa

    Java執行面試知識點彙總(超詳細總結

    一、sleep()方法、wait()方法、yeild()方法、interrupt()方法、notify()、notifyAll()方法 1、sleep()方法: sleep方法為Thread的靜態方法; sleep方法的作用是讓執行緒休眠指定時間,在時間到

    Java執行(二) —— 執行安全、執行同步、執行間通訊(試題集)

    上一篇博文:Java多執行緒(一) —— 執行緒的狀態詳解中詳細介紹了執行緒的五種狀態及狀態間的轉換。本文著重介紹了執行緒安全的相關知識點,包括執行緒同步和鎖機制、執行緒間通訊以及相關面試題的總結 一、執行緒安全 多個執行緒在執行同一段程式碼的時候,每次的執行結果和單執行緒執行的結果都是一樣的,不存在執行結果

    Java 執行試題答案(非常全面)

    這篇文章主要是對多執行緒的問題進行總結的,因此羅列了40個多執行緒的問題。 這些多執行緒的問題,有些來源於各大網站、有些來源於自己的思考。可能有些問題網上有、可能有些問題對應的答案也有、也可能有些各位網友也都看過,但是本文寫作的重心就是所有的問題都會按照自己的理解回答一遍,不會去看網上的

    Java執行試題整理(BATJ都愛問)

    今天給大家總結一下,面試中出鏡率很高的幾個多執行緒面試題,希望對大家學習和麵試都能有所幫助。備註:文中的程式碼自己實現一遍的話效果會更佳哦! 一、面試中關於 synchronized 關鍵字的 5 連擊 1.1 說一說自己對於 synchronized 關鍵字的瞭解 synchroniz

    java執行試題小結

    http://www.importnew.com/12773.html http://www.cnblogs.com/fingerboy/p/5352880.html https://blog.csdn.net/ll666634/article/details/78615505 https://blog