2020最新Java面試題(常見面試題及答案彙總)

Java基礎篇

點此原文連接!

1. JDK 和 JRE 的區別是什麼?

  • JDK:Java Development Kit 的簡稱,java 開發工具包,提供了 java 的開發環境和運行環境。
  • JRE:Java Runtime Environment 的簡稱,java 運行環境,爲 java 的運行提供了所需環境。

總結: JDK 包含了 JRE,同時還包含了編譯 java 源碼的編譯器 javac,還包含了不少 java 程序調試和分析的工具。簡單來講:若是你須要運行 java 程序,只需安裝 JRE 就能夠了,若是你須要編寫 java 程序,就須要安裝 JDK。java

2. Java中有哪些數據類型?

Java中數據類型分爲基本數據類型和引用數據類型2種web

  • 基本類型:byte(默認值0,佔1字節)、short(默認值0,佔2字節)、int(默認值0,佔4字節)、long(默認值0,佔8字節)、float(默認值0.0,佔4字節)、double(默認值0.0,佔8字節)、char(默認值\u0000,佔2字節)、boolean(默認值false)
  • 引用類型:類(默認值null)、接口(默認值null)、數組(默認值null)

3. == 和 equals 的區別是什麼?

基本類型和引用類型比較,== 的做用效果是不一樣的。算法

  • 基本類型:比較的是值是否相同設計模式

  • 引用類型:比較的是引用是否相同數組

    int x = 10;
    int y = 10;
    String a = "panda";
    String b = "panda";
    String c = new String("panda");
    // true 基本類型比較值是否相同
    System.out.println(x == y);
    // true 引用類型比較引用是否相同,這裏引用相同
    System.out.println(a == b);
    // false 引用不一樣
    System.out.println(a == c);
    // true 引用不一樣,String重寫了equals,使其用值比較
    System.out.println(a.equals(c));
    複製代碼

    12345678910111213緩存

equals 本質上就是 ==,Object類中定義的 equals 方法以下安全

public boolean equals(Object obj) {
        return (this == obj);
    }
123
複製代碼

總結:== 對於基本類型比較的是值,對於引用類型比較的是引用;而 equals 默認狀況下是引用比較,只是不少類重寫了 equals 方法,好比 String、Integer 等把它變成了值比較,因此通常狀況下 equals 比較的是值是否相等。服務器

4. 兩個對象的 hashCode() 相同,則 equals() 也必定爲 true 正確嗎?

不正確,兩個對象的 hashCode() 相同,equals() 不必定 true。好比在 map 中,hashCode() 相等,只能說明這兩個鍵值對的哈希值相同,不表明這兩個鍵值對相等。markdown

String str1 = "通話";
	String str2 = "重地";
	// str1: 1179395 | str2: 1179395
	System.out.println(String.format("str1: %d | str2: %d",str1.hashCode(),str2.hashCode()));
	// false
	System.out.println(str1.equals(str2));
123456
複製代碼

5. Java 中 final 關鍵字的做用是什麼?

  • final 修飾的類叫最終類,不能被繼承
  • final 修飾的方法叫最終方法,不能被重寫,但能夠被繼承
  • final 修飾的變量叫常量,必須初始化,初始化以後值不能被修改

6. String、StringBuffer、StringBuilder 的區別是什麼?

String 是字符串常量,每次操做都會生產新的對象,適用於少許字符串操做的狀況;StringBuffer、StringBuilder 是字符串變量,StringBuffer 是線程安全的,而 StringBuilder 是非線程安全的,但 StringBuilder 的性能卻高於 StringBuffer,因此在單線程環境下推薦使用 StringBuilder,多線程環境下推薦使用 StringBuffer。數據結構

7. String str=「donkey」 與 String str=new String(「donkey」) 同樣嗎?

不同,由於內存的分配方式不同。String str=「donkey」,java 虛擬機會將其分配到常量池中;而 String str=new String(「donkey」) 則會被分到堆內存中。

8. 字符串如何反轉?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法

StringBuffer stringBuffer = new StringBuffer();
	stringBuffer.append("abcdefg");
	System.out.println(stringBuffer.reverse()); // gfedcba
	StringBuilder stringBuilder = new StringBuilder();
	stringBuilder.append("abcdefg");
	System.out.println(stringBuilder.reverse()); // gfedcba
123456
複製代碼

9. String 類中經常使用方法都有哪些?

String str = " app le ";
	// indexOf(): 返回指定字符的索引
	System.out.println(str.indexOf("a")); // 1
	// charAt(): 返回指定索引處的字符
	System.out.println(str.charAt(5)); // l
	// replace(): 字符串替換
	System.out.println(str.replace("pp", "cc")); // " acc le "
	// trim(): 去除字符串兩端空白
	System.out.println(str.trim()); // "app le"
	// split(): 分割字符串,返回一個分割後的字符串數組
	String[] arr = str.split(" ");
	// getBytes(): 返回字符串的 byte 類型數組
	byte[] bytes = str.getBytes();
	// length(): 返回字符串長度
	System.out.println(str.length()); // 8
	// toLowerCase(): 將字符串轉成小寫字母
	System.out.println(str.toLowerCase()); // " app le "
	// toUpperCase(): 將字符串轉成大寫字符
	System.out.println(str.toUpperCase()); // " APP LE "
	// substring(): 截取字符串
	System.out.println(str.substring(2)); // "pp le "
	// equals(): 字符串比較
	System.out.println("apple".equals(str)); // false
1234567891011121314151617181920212223
複製代碼

10. 抽象類是什麼?

擁有抽象方法(指沒有方法體的方法,同時抽象方法還必須使用關鍵字abstract 作修飾)的類就是抽象類,抽象類要使用 abstract 關鍵字聲明。

11. 抽象類必需要有抽象方法嗎?

不須要,抽象類不必定非要有抽象方法,以下代碼能夠正常運行

public abstract class elephant {
    String str = "apple";
    public void test01(){
        System.out.println("aaaa");
    }
}
123456
複製代碼

12. 普通類和抽象類的區別是什麼?

  • 普通類不能包含抽象方法,抽象類能夠有抽象方法
  • 普通類能夠直接實例化,抽象類不能直接實例化

13. 抽象類能使用 final 修飾嗎?

不能,定義抽象類就是讓其餘類繼承的,若是定義爲 final 該類就不能被繼承,這樣彼此就會產生矛盾,因此 final 不能修飾抽象類。

14. 接口是什麼?

接口能夠理解爲一種特殊的類,裏面所有是由全局常量和公共的抽象方法所組成。接口是解決Java沒法使用多繼承的一種手段,可是接口在實際中更多的做用是制定標準。

15. 接口和抽象類的區別是什麼?

  • 接口必須使用 implements 來實現接口;抽象類的子類使用 extends 來繼承
  • 接口不能有構造函數;抽象類能夠有構造函數
  • 一個類只能繼承一個抽象類,但能夠實現多個接口
  • 抽象類中成員變量默認 default,可在子類中被從新定義,也可被從新賦值,抽象方法被 abstract 修飾,不能被 private、static、synchronized 和 native 等修飾;接口中成員變量默認爲 public static final 修飾,必須賦初值,不能被修改,其全部的成員方法默認使用 public abstract 修飾的

16. Java 中 IO 流分幾種?

  • 按功能分:輸入流(input)、輸出流(output)
  • 按類型分:字節流、字符流

字節流和字符流的區別:字節流按 8 位傳輸,以字節爲單位輸入輸出數據;字符流按 16 位傳輸,以字符爲單位輸入輸出數據。

17. BIO、NIO、AIO 有什麼區別?

  • BIO:同步阻塞 IO,就是咱們日常使用的傳統 IO,服務器的實現模式是一個請求鏈接一個線程,併發處理能力低,可能形成沒必要要的線程開銷,嚴重的還將致使服務器內存溢出。
  • NIO:同步非阻塞 IO,是傳統 IO 的升級,服務器的實現模式是多個請求一個線程,即請求會註冊到多路複用器Selector上,多路複用器輪詢到鏈接有IO請求時才啓動一個線程處理。
  • AIO:異步非阻塞 IO,是 NIO 的升級,也叫 NIO2,服務器的實現模式爲多個有效請求一個線程,客戶端的IO請求都是由OS先完成再通知服務器應用去啓動線程處理(回調)。

18. Files 的經常使用方法都有哪些?

// Files.exists():檢測文件路徑是否存在
	Path path1 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test");
	System.out.println(Files.exists(path1, new LinkOption[]{LinkOption.NOFOLLOW_LINKS})); // true
	// Files.createFile():建立文件
	Path path2 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\a.txt");
	try {
	    Path newFilw = Files.createFile(path2);
	} catch (FileAlreadyExistsException e){
	    System.out.println("exists");
	} catch (IOException e) {
	    System.out.println("other wrong");
	}
	// Files.createDirectory():建立文件夾
	Path path3 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\newDirectory");
	try {
	    Path newDirectory = Files.createDirectory(path3);
	} catch (FileAlreadyExistsException e) {
	    System.out.println("exists");
	} catch (IOException e){
	    System.out.println("other wrong");
	}
	// Files.delete():刪除一個文件或目錄
	try {
	    Files.delete(path2);
	} catch (IOException e) {
	    e.printStackTrace();
	}
	// Files.copy():複製文件
	Path source = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\b.txt");
	Path target = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\newb.txt");
	try {
	    Files.copy(source,target);
	} catch (FileAlreadyExistsException e) {
	    System.out.println("targetFile already exists");
	} catch (IOException e){
	    System.out.println("other wrong");
	}
	// Files.move():移動文件
	Path source1 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\b.txt");
	Path target1 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\newDirectory\\a.txt");
	try {
	    Files.move(source1,target1);
	} catch (FileAlreadyExistsException e) {
	    System.out.println("targetFile1 already exists");
	} catch (IOException e){
	    System.out.println("other wrong");
	}
	// Files.size():查看文件個數
	// Files.read():讀取文件
	// Files.write():寫入文件
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
複製代碼

Java 容器篇

1. Java 中都有哪些容器?

在這裏插入圖片描述

2. Collection 和 Collections 的區別是什麼?

  • java.util.Collection 是一個集合接口(集合類的一個頂級接口)。它提供了對集合對象進行基本操做的通用接口方法。Collection 接口在 Java 類庫中有不少具體的實現。Collection 接口的意義是爲各類具體的集合提供了最大化的統一操做方式,其直接繼承接口有 List 與 Set。
  • Collections 則是集合類的一個工具類,其中提供了一系列靜態方法,用於對集合中元素進行排序、搜索以及線程安全等各類操做。

3. List、Set、Map 之間的區別是什麼?

在這裏插入圖片描述

4. HashMap 和 Hashtable 的區別是什麼?

  • hashMap 去除了 HashTable 的 contains() 方法,可是加上了 containsValue() 和 containsKey() 方法。
  • hashTable 是線程同步的,而 HashMap 是非同步的,HashMap 效率上比 hashTable 要高。
  • hashMap 容許空鍵空值,而 hashTable 不容許。

5. 如何決定使用 HashMap 仍是 TreeMap?

在 Map 中作插入、刪除和定位元素這類操做,HashMap 是最好的選擇。假如你須要對一個有序的 key 集合進行遍歷,TreeMap 是更好的選擇。

6. HashMap 的實現原理是什麼?

HashMap概述: HashMap 是基於哈希表的Map接口的非同步實現。此實現提供全部可選的映射操做,並容許使用 null 值和 null 鍵。此類不保證映射的順序,特別是它不保證該順序恆久不變。

HashMap的數據結構: HashMap 其實是一個「鏈表散列」的數據結構,即數組和鏈表的結合體。

當咱們往 HashMap 中 put 元素時,首先根據 key 的 hashcode 從新計算 hash 值,根據 hash 值獲得這個元素在數組中的位置(下標),若是該數組在該位置上已經存放了其餘元素,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最早加入的放入鏈尾。若是數組中該位置沒有元素,就直接將該元素放到數組的該位置上。

Jdk1.8 中對 HashMap 的實現作了優化,當鏈表中的節點數據超過八個以後,該鏈表會轉爲紅黑樹來提升查詢效率,從原來的 O(n) 到 O(logn)。

7. HashSet 的實現原理是什麼?

HashSet 實現 Set 接口,由哈希表(其實是一個 HashMap 實例)支持。它不保證 set 的迭代順序;特別是它不保證該順序恆久不變;此類容許使用 null 元素;HashSet 中不容許有重複元素。

HashSet 是基於 HashMap 實現的,HashSet 中的元素都存放在 HashMap 的 key 上面,而 value 中的值都是統一的一個 private static final Object PRESENT = new Object()。HashSet跟HashMap同樣,都是一個存放鏈表的數組。

8. ArrayList 和 LinkedList 的區別是什麼?

ArrayList 底層基於動態數組,隨機訪問元素效率高,向集合尾部添加元素效率高,刪除或者在其餘位置添加元素效率低(須要移動數組);LinkedList 基於鏈表的動態數組,數據添加和刪除效率高,只須要改變指針指向便可,可是訪問數據的平均效率低,須要對鏈表進行遍歷。

9. 如何實現數組和 List 之間的轉換?

// List 轉 數組
	String[] strArr = {"apple","pear","banana","peach"};
	List<String> list = Arrays.asList(strArr);
	// 數組 轉 List
	String[] arr = (String[]) list.toArray();
12345
複製代碼

10. ArrayList 和 Vector 的區別是什麼?

  • Vector 是同步的,線程安全,而 ArrayList 不是。所以,ArrayList 的性能要高於Vector,若是你想在迭代的時候對列表進行改變,應該使用 CopyOnWriteArrayList。
  • 超過初始容量時,ArrayList 擴容1.5倍,Vector 擴容2倍。

11. Array 和 ArrayList 的區別是什麼?

  • Array 能夠容納基本類型和對象,而 ArrayList 只能容納對象。
  • Array 是指定大小的,而 ArrayList 初始化大小是固定的。
  • Array 沒有提供 ArrayList 那麼多功能,好比addAll、removeAll和iterator等。

12. 在 Queue 中 poll() 和 remove() 的區別是什麼?

Queue 中 remove() 和 poll() 都是用來從隊列頭部刪除一個元素,在隊列元素爲空的狀況下,remove() 方法會拋出 NoSuchElementException 異常,poll() 方法只會返回 null。

13. 線程安全的集合類有哪些?

  • vector:就比 ArrayList 多了個同步化機制(線程安全),由於效率較低,如今已經不太建議使用
  • statck:堆棧類,先進後出
  • hashtable:就比 HashMap 多了個線程安全
  • enumeration:枚舉,至關於迭代器

14. 迭代器 Iterator 是什麼?

迭代器是一種設計模式,它是一個對象,它能夠遍歷並選擇序列中的對象,而開發人員不須要了解該序列的底層結構。迭代器一般被稱爲「輕量級」對象,由於建立它的代價小。

15. Iterator 怎麼使用?有什麼特色?

  • Iterator 功能比較簡單,而且只能單向移動

  • 使用方法 iterator() 要求容器返回一個 Iterator。第一次調用 Iterator 的 next() 方法時,它返回序列的第一個元素。注意:iterator() 方法是 java.lang.Iterable 接口,被 Collection 繼承。

  • 使用 hasNext() 檢查序列中是否還有元素

  • 使用 next() 得到序列中的下一個元素

  • 使用 remove() 將迭代器新返回的元素刪除

  • Iterator 是 Java 迭代器最簡單的實現,爲 List 設計的 ListIterator 具備更多的功能,它能夠從兩個方向遍歷 List,也能夠從 List 中插入和刪除元素。

    public static void main(String[] args) {
        // List
        ArrayList<String> list = new ArrayList<>();
        list.add("apple");
        list.add("pear");
        list.add("banana");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String s = iterator.next();
            if ("apple".equals(s)){
                iterator.remove();
            }
        }
        list.forEach(item -> System.out.println(item));
    
        // Map<key,value>
        Map<String,String> map=new HashMap<>();
        map.put("pig","豬");
        map.put("cat","貓");
        map.put("dog","狗");
        Iterator<String> iterator1 = map.keySet().iterator();
        Iterator<String> iterator2 = map.values().iterator();
        while (iterator1.hasNext()){
            System.out.println(iterator1.next());
        }
        while (iterator2.hasNext()){
            System.out.println(iterator2.next());
        }
    }
    複製代碼

    1234567891011121314151617181920212223242526272829

16. Iterator 和 ListIterator 的區別是什麼?

  • Iterator 可用來遍歷 Set 和 List 集合,可是 ListIterator 只能用來遍歷 List。
  • Iterator 對集合只能是前向遍歷,ListIterator 既能夠前向也能夠後向。
  • ListIterator 實現了 Iterator 接口,幷包含其餘的功能,好比:增長元素,替換元素,獲取前一個和後一個元素的索引等等。

Java多線程

1. 並行和併發的區別是什麼?

  • 並行是指兩個或者多個事件在同一時刻發生;而併發是指兩個或多個事件在同一時間間隔發生
  • 並行是在不一樣實體上的多個事件,併發是在同一實體上的多個事件
  • 在一臺處理器上「同時」處理多個任務,在多臺處理器上同時處理多個任務。如 hadoop 分佈式集羣

普通解釋:

  • 併發:交替作不一樣事情的能力
  • 並行:同時作不一樣事情的能力

專業術語:

  • 併發:不一樣的代碼塊交替執行
  • 並行:不一樣的代碼塊同時執行

2. 線程和進程的區別是什麼?

進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程。進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存資源,減小切換次數,從而效率更高。線程是進程的一個實體,是 cpu 調度和分派的基本單位,是比程序更小的能獨立運行的基本單位。同一進程中的多個線程之間能夠併發執行。

3. 守護線程是什麼?

守護線程(即 daemon thread),是個服務線程,準確地來講就是服務其餘的線程。它可以自我結束。若是 JVM 中沒有一個正在運行的非守護線程,這個時候,JVM 會退出。JVM 中的垃圾回收線程就是典型的守護線程,若是說沒有守護線程,JVM 就永遠不會退出了

4. 建立線程有哪幾種方式?

① 繼承 Thread 類建立線程

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("run task");
    }
}
public class Demo {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}
123456789101112
複製代碼

② 實現 Runnable 接口建立線程

public class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println("run task");
    }
}
public class Demo {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread);
        thread.start();
    }
}
12345678910111213
複製代碼

③ 經過 Callable 和 Future 建立線程

public class MyThread implements Callable {
    @Override
    public Object call() {
        System.out.println("run!");
        return "run task success";
    }
}
public class Demo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread myThread = new MyThread();
        FutureTask futureTask = new FutureTask<String>(myThread);
        Thread thread = new Thread(futureTask);
        thread.start();
        // 得到子線程執行結束後的返回值
        System.out.println(futureTask.get()); // run task success
    }
}
1234567891011121314151617
複製代碼

5. 說一下 runnable 和 callable 有什麼區別?

  • Runnable 接口中的 run() 方法的返回值是 void,它作的事情只是純粹地去執行run()方法中的代碼
  • Callable 接口中的 call() 方法是有返回值的,是一個泛型,和 Future、FutureTask 配合能夠用來獲取異步執行的結果

6. 線程有哪些狀態?

線程一般都有五種狀態,建立、就緒、運行、阻塞和死亡。

  • 建立狀態:在生成線程對象,並無調用該對象的start方法,這時線程處於建立狀態。
  • 就緒狀態:當調用了線程對象的 start 方法以後,該線程就進入了就緒狀態,可是此時線程調度程序尚未把該線程設置爲當前線程,此時處於就緒狀態。在線程運行以後,從等待或者睡眠中回來以後,也會處於就緒狀態。
  • 運行狀態:線程調度程序將處於就緒狀態的線程設置爲當前線程,此時線程就進入了運行狀態,開始運行 run 函數當中的代碼。
  • 阻塞狀態:線程正在運行的時候,被暫停,一般是爲了等待某個事件的發生(好比說某項資源就緒)以後再繼續運行。sleep,suspend,wait 等方法均可以致使線程阻塞。
  • 死亡狀態:若是一個線程的 run 方法執行結束或者調用 stop 方法後,該線程就會死亡。對於已經死亡的線程,沒法再使用 start 方法令其進入就緒。

7. sleep() 和 wait() 有什麼區別?

  • sleep():是線程類(Thread)的靜態方法,讓調用線程進入睡眠狀態,讓出執行機會給其餘線程,等到休眠時間結束後,線程進入就緒狀態和其餘線程一塊兒競爭cpu的執行時間。由於 sleep() 是 static 靜態的方法,他不能改變對象的鎖,當一個 synchronized 塊中調用了 sleep() 方法,線程雖然進入休眠,可是對象的鎖沒有被釋放,其餘線程依然沒法訪問這個對象。
  • wait():是 Object 類的方法,當一個線程執行到 wait 方法時,它就進入到一個和該對象相關的等待池,同時釋放對象的鎖,使其餘線程可以訪問,能夠經過 notify,notifyAll 方法來喚醒等待的線程。

8. notify() 和 notifyAll() 有什麼區別?

若是線程調用了對象的 wait() 方法,那麼線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。

當有線程調用了對象的 notifyAll() 方法(喚醒全部 wait 線程)或 notify() 方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了 notify 後只有一個線程會由等待池進入鎖池,而 notifyAll 會將該對象等待池內的全部線程移動到鎖池中,等待鎖競爭。

優先級高的線程競爭到對象鎖的機率大,倘若某線程沒有競爭到該對象鎖,它還會留在鎖池中,惟有線程再次調用 wait() 方法,它纔會從新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。

9. run() 和 start() 有什麼區別?

每一個線程都是經過某個特定 Thread 對象所對應的方法 run() 來完成其操做的,方法 run() 稱爲線程體。經過調用 Thread 類的 start() 方法來啓動一個線程。

  • start() 方法來啓動一個線程,真正實現了多線程運行。這時無需等待 run 方法體代碼執行完畢,能夠直接繼續執行下面的代碼; 這時此線程是處於就緒狀態, 並無運行。 而後經過此 Thread 類調用方法 run() 來完成其運行狀態, 這裏方法 run() 稱爲線程體,它包含了要執行的這個線程的內容,run 方法運行結束, 此線程終止。而後CPU再調度其它線程。
  • run() 方法是在本線程裏的,只是線程裏的一個函數,而不是多線程的。 若是直接調用 run(),其實就至關因而調用了一個普通函數而已,直接調用 run() 方法必須等待 run() 方法執行完畢才能執行下面的代碼,因此執行路徑仍是隻有一條,根本就沒有線程的特徵,因此在多線程執行時要使用 start() 方法而不是 run() 方法。

10. 建立線程池有哪幾種方式?

  • newFixedThreadPool(int nThreads):建立一個固定長度的線程池,每當提交一個任務就建立一個線程,直到達到線程池的最大數量,這時線程規模將再也不變化,當線程發生未預期的錯誤而結束時,線程池會補充一個新的線程。
  • newCachedThreadPool():建立一個可緩存的線程池,若是線程池的規模超過了處理需求,將自動回收空閒線程,而當需求增長時,則能夠自動添加新線程,線程池的規模不存在任何限制。
  • newSingleThreadExecutor():這是一個單線程的 Executor,它建立單個工做線程來執行任務,若是這個線程異常結束,會建立一個新的來替代它;它的特色是能確保依照任務在隊列中的順序來串行執行。
  • newScheduledThreadPool(int corePoolSize):建立一個固定長度的線程池,並且以延遲或定時的方式來執行任務,相似於Timer。

11. 線程池都有哪些狀態?

線程池有5種狀態:Running、ShutDown、Stop、Tidying、Terminated。

  • Running:這是最正常的狀態,接受新的任務,處理等待隊列中的任務。線程池的初始化狀態是Running。線程池被一旦被建立,就處於 Running 狀態,而且線程池中的任務數爲 0
  • ShutDown:不接受新的任務提交,可是會繼續處理等待隊列中的任務。調用線程池的 shutdown() 方法時,線程池由Running -> ShutDown。
  • Stop:不接受新的任務提交,再也不處理等待隊列中的任務,中斷正在執行任務的線程。調用線程池的shutdownNow() 方法時,線程池由 (Running or ShutDown) -> Stop。
  • Tidying:全部的任務都銷燬了,workCount 爲 0,線程池的狀態在轉換爲 Tidying 狀態時,會執行鉤子方法 terminated()。由於 terminated() 在 ThreadPoolExecutor 類中是空的,因此用戶想在線程池變爲 Tidying 時進行相應的處理;能夠經過重載 terminated() 函數來實現。 當線程池在 ShutDown 狀態下,阻塞隊列爲空而且線程池中執行的任務也爲空時,就會由 ShutDown -> Tidying。當線程池在Stop 狀態下,線程池中執行的任務爲空時,就會由Stop -> Tidying。
  • Terminated:線程池處在 Tidying 狀態時,執行完 terminated() 以後,就會由 Tidying -> Terminated。

線程池各個狀態切換框架圖:
在這裏插入圖片描述

12. 線程池中 submit() 和 execute() 方法有什麼區別?

  • 接收的參數不同

    submit(Runnable task) submit(Runnable task,T result) submit(Callable task) execute(Runnable command) 1234

  • submit() 有返回值,而 execute() 沒有。用到返回值的例子,好比說我有不少個作 validation 的task,我但願全部的 task 執行完,而後每一個 task 告訴我它的執行結果,是成功仍是失敗,若是是失敗,緣由是什麼。

  • submit()方便 Exception 處理,若是你在你的 task 裏會拋出 checked 或者 unchecked exception,而你又但願外面的調用者可以感知這些 exception 並作出及時的處理,那麼就須要用到submit,經過捕獲 Future.get 拋出的異常。

13. java 程序中怎麼保證多線程的運行安全?

線程安全在三個方面體現:

  • 原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操做(atomic,synchronized)
  • 可見性:一個線程對主內存的修改能夠及時地被其餘線程看到(synchronized,volatile)
  • 有序性:一個線程觀察其餘線程中的指令執行順序,因爲指令重排序,該觀察結果通常雜亂無序(happens-before 原則)

14. 多線程鎖的升級原理是什麼?

在 Java 中,鎖共有 4 種狀態,級別從低到高依次爲:無狀態鎖,偏向鎖,輕量級鎖和重量級鎖狀態,這幾個狀態會隨着競爭狀況逐漸升級。鎖能夠升級但不能降級。

鎖升級的圖示過程:
在這裏插入圖片描述

15. 什麼是死鎖?

死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。是操做系統層面的一個錯誤,是進程死鎖的簡稱,最先在 1965 年由 Dijkstra 在研究銀行家算法時提出的,它是計算機操做系統乃至整個併發程序設計領域最難處理的問題之一。

16. 怎麼防止死鎖?

死鎖的四個必要條件:

  • 互斥條件:進程對所分配到的資源不容許其餘進程進行訪問,若其餘進程訪問該資源,只能等待,直至佔有該資源的進程使用完成後釋放該資源
  • 請求和保持條件:進程得到必定的資源以後,又對其餘資源發出請求,可是該資源可能被其餘進程佔有,此時請求阻塞,但又對本身得到的資源保持不放
  • 不可剝奪條件:是指進程已得到的資源,在未完成使用以前,不可被剝奪,只能在使用完後本身釋放
  • 環路等待條件:是指進程發生死鎖後,若干進程之間造成一種頭尾相接的循環等待資源關係

這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之 一不知足,就不會發生死鎖。

理解了死鎖的緣由,尤爲是產生死鎖的四個必要條件,就能夠最大可能地避免、預防和 解除死鎖。因此,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確 定資源的合理分配算法,避免進程永久佔據系統資源。此外,也要防止進程在處於等待狀態的狀況下佔用資源。所以,對資源的分配要給予合理的規劃。

17. ThreadLocal 是什麼?有哪些使用場景?

線程局部變量是侷限於線程內部的變量,屬於線程自身全部,不在多個線程間共享。Java 提供ThreadLocal 類來支持線程局部變量,是一種實現線程安全的方式。可是在管理環境下(如 web 服務器)使用線程局部變量的時候要特別當心,在這種狀況下,工做線程的生命週期比任何應用變量的生命週期都要長。任何線程局部變量一旦在工做完成後沒有釋放,Java 應用就存在內存泄露的風險。

18. 說一下 synchronized 底層實現原理?

synchronized 能夠保證方法或者代碼塊在運行時,同一時刻只有一個方法能夠進入到臨界區,同時它還能夠保證共享變量的內存可見性。

Java 中每個對象均可以做爲鎖,這是 synchronized 實現同步的基礎:

  • 普通同步方法,鎖是當前實例對象
  • 靜態同步方法,鎖是當前類的class對象
  • 同步方法塊,鎖是括號裏面的對象

19. synchronized 和 volatile 的區別是什麼?

  • volatile 本質是在告訴 jvm 當前變量在寄存器(工做內存)中的值是不肯定的,須要從主存中讀取; synchronized 則是鎖定當前變量,只有當前線程能夠訪問該變量,其餘線程被阻塞住。
  • volatile 僅能使用在變量級別;synchronized 則可使用在變量、方法、和類級別的。
  • volatile 僅能實現變量的修改可見性,不能保證原子性;而 synchronized 則能夠保證變量的修改可見性和原子性。
  • volatile 不會形成線程的阻塞;synchronized 可能會形成線程的阻塞。
  • volatile 標記的變量不會被編譯器優化;synchronized 標記的變量能夠被編譯器優化。

20. synchronized 和 Lock 有什麼區別?

  • synchronized 是 java 內置關鍵字,在 jvm 層面,Lock 是個 java 類
  • synchronized 沒法判斷是否獲取鎖的狀態,Lock 能夠判斷是否獲取到鎖
  • synchronized 會自動釋放鎖(a 線程執行完同步代碼會釋放鎖 ;b 線程執行過程當中發生異常會釋放鎖),Lock 需在 finally 中手工釋放鎖(unlock() 方法釋放鎖),不然容易形成線程死鎖
  • 用 synchronized 關鍵字的兩個線程 1 和線程 2 ,若是當前線程 1 得到鎖,線程 2 等待。若是線程 1阻塞,線程 2 則會一直等待下去,而 Lock 鎖就不必定會等待下去,若是嘗試獲取不到鎖,線程能夠不用一直等待就結束了
  • synchronized 的鎖可重入、不可中斷、非公平,而 Lock鎖 可重入、可判斷、可公平(二者皆可)
  • Lock 鎖適合大量同步的代碼的同步問題,synchronized 鎖適合代碼少許的同步問題

21. synchronized 和 ReentrantLock 區別是什麼?

synchronized 是和 if、else、for、while 同樣的關鍵字,ReentrantLock 是類,這是兩者的本質區別。既然 ReentrantLock 是類,那麼它就提供了比 synchronized 更多更靈活的特性,能夠被繼承、能夠有方法、能夠有各類各樣的類變量,ReentrantLock 比 synchronized 的擴展性體如今幾點上:

  • ReentrantLock 能夠對獲取鎖的等待時間進行設置,這樣就避免了死鎖
  • ReentrantLock 能夠獲取各類鎖的信息
  • ReentrantLock 能夠靈活地實現多路通知

另外,兩者的鎖機制其實也是不同的:ReentrantLock 底層調用的是 Unsafe 的 park 方法加鎖,synchronized 操做的應該是對象頭中 mark word。

22. 說一下 atomic 的原理?

Atomic 包中的類基本的特性就是在多線程環境下,當有多個線程同時對單個(包括基本類型及引用類型)變量進行操做時,具備排他性,即當多個線程同時對該變量的值進行更新時,僅有一個線程能成功,而未成功的線程能夠向自旋鎖同樣,繼續嘗試,一直等到執行成功。

Atomic 系列的類中的核心方法都會調用 unsafe 類中的幾個本地方法。咱們須要先知道一個東西就是Unsafe 類,全名爲:sun.misc.Unsafe,這個類包含了大量的對 C 代碼的操做,包括不少直接內存分配以及原子操做的調用,而它之因此標記爲非安全的,是告訴你這個裏面大量的方法調用都會存在安全隱患,須要當心使用,不然會致使嚴重的後果,例如在經過 unsafe 分配內存的時候,若是本身指定某些區域可能會致使一些相似 C++ 同樣的指針越界到其餘進程的問題。

相關文章
相關標籤/搜索