2020JAVA面試題附答案(持續更新版)


文章目錄
前言
1.JAVA基礎
2.JVM 知識
3.開源框架知識
4.操作系統
5.多線程
6.數據庫知識
7.最后附一遍持續整理的博客
前言
前言:
少年易老學難成,一寸光陰不可輕。未覺池塘春草夢,階前梧葉已秋聲 。—朱熹《勸學詩》
勤奮才是改變你命運的唯一捷徑。
整理不易,各位看官點贊再看更舒適,養成好習慣(●´∀`●)

1.JAVA基礎
1.JAVA中的幾種基本類型,各占用多少字節?

下圖單位是bit,非字節 1B=8bit


2.String能被繼承嗎?為什么?
不可以,因為String類有final修飾符,而final修飾的類是不能被繼承的,實現細節不允許改變。平常我們定義的String str=”abc”(直接賦一個字面量);其實和String str=new String(“abc”)(通過構造器構造)還是有差異的。


String str=“abc”和String str=new String(“abc”); 產生幾個對象?
1.前者1或0,后者2或1,先看字符串常量池,如果字符串常量池中沒有,都在常量池中創建一個,如果有,前者直接引用,后者在堆內存中還需創建一個“abc”實例對象。
2.對於基礎類型的變量和常量:變量和引用存儲在棧中,常量存儲在常量池中。
3.為了提升jvm(JAVA虛擬機)性能和減少內存開銷,避免字符的重復創建 項目中還是不要使用new String去創建字符串,最好使用String直接賦值。
1
2
3
4
參考鏈接
3.String, Stringbuffer, StringBuilder 的區別。
String 字符串常量(final修飾,不可被繼承),String是常量,當創建之后即不能更改。(可以通過StringBuffer和StringBuilder創建String對象(常用的兩個字符串操作類)。)
==StringBuffer 字符串變量(線程安全),==其也是final類別的,不允許被繼承,其中的絕大多數方法都進行了同步處理,包括常用的Append方法也做了同步處理(synchronized修飾)。其自jdk1.0起就已經出現。其toString方法會進行對象緩存,以減少元素復制開銷。

public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
1
2
3
4
5
6
==StringBuilder 字符串變量(非線程安全)==其自jdk1.5起開始出現。與StringBuffer一樣都繼承和實現了同樣的接口和類,方法除了沒使用synch修飾以外基本一致,不同之處在於最后toString的時候,會直接返回一個新對象。

public String toString() {
// Create a copy, don’t share the array
return new String(value, 0, count);
}
1
2
3
4
4.ArrayList 和 LinkedList 有什么區別。
ArrayList和LinkedList都實現了List接口,有以下的不同點:
1、ArrayList是基於索引的數據接口,它的底層是數組。它可以以O(1)時間復雜度對元素進行隨機訪問。與此對應,LinkedList是以元素列表的形式存儲它的數據,每一個元素都和它的前一個和后一個元素鏈接在一起,在這種情況下,查找某個元素的時間復雜度是O(n)。
2、相對於ArrayList,LinkedList的插入,添加,刪除操作速度更快,因為當元素被添加到集合任意位置的時候,不需要像數組那樣重新計算大小或者是更新索引。
3、LinkedList比ArrayList更占內存,因為LinkedList為每一個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。
5.講講類的實例化順序,比如父類靜態數據,構造函數,字段,子類靜態數據,構造函數,字段,當 new 的時候, 他們的執行順序。
此題考察的是類加載器實例化時進行的操作步驟(加載–>連接->初始化)。
父類靜態代變量、
父類靜態代碼塊、
子類靜態變量、
子類靜態代碼塊、
父類非靜態變量(父類實例成員變量)、
父類構造函數、
子類非靜態變量(子類實例成員變量)、
子類構造函數。
測試demo
參閱博客《深入理解類加載》
6.用過哪些 Map 類,都有什么區別,HashMap 是線程安全的嗎,並發下使用的 Map 是什么,他們內部原理分別是什么,比如存儲方式, hashcode,擴容, 默認容量等。
hashMap是線程不安全的,HashMap是數組+鏈表+紅黑樹(JDK1.8增加了紅黑樹部分)實現的,采用哈希表來存儲的,
參考鏈接
JAVA8 的 ConcurrentHashMap 為什么放棄了分段鎖,有什么問題嗎,如果你來設計,你如何設計。
參考鏈接
7.有沒有有順序的 Map 實現類, 如果有, 他們是怎么保證有序的。
TreeMap和LinkedHashMap是有序的(TreeMap默認升序,LinkedHashMap則記錄了插入順序)。
參考鏈接
8.抽象類和接口的區別,類可以繼承多個類么,接口可以繼承多個接口么,類可以實現多個接口么。
1、抽象類和接口都不能直接實例化,如果要實例化,抽象類變量必須指向實現所有抽象方法的子類對象,接口變量必須指向實現所有接口方法的類對象。
2、抽象類要被子類繼承,接口要被類實現。
3、接口只能做方法申明,抽象類中可以做方法申明,也可以做方法實現
4、接口里定義的變量只能是公共的靜態的常量,抽象類中的變量是普通變量。
5、抽象類里的抽象方法必須全部被子類所實現,如果子類不能全部實現父類抽象方法,那么該子類只能是抽象類。同樣,一個實現接口的時候,如不能全部實現接口方法,那么該類也只能為抽象類。
6、抽象方法只能申明,不能實現。abstract void abc();不能寫成abstract void abc(){}。
7、抽象類里可以沒有抽象方法
8、如果一個類里有抽象方法,那么這個類只能是抽象類
9、抽象方法要被實現,所以不能是靜態的,也不能是私有的。
10、接口可繼承接口,並可多繼承接口,但類只能單根繼承。
9.繼承和聚合的區別在哪。
繼承指的是一個類(稱為子類、子接口)繼承另外的一個類(稱為父類、父接口)的功能,並可以增加它自己的新功能的能力,繼承是類與類或者接口與接口之間最常見的關系;在Java中此類關系通過關鍵字extends明確標識,在設計時一般沒有爭議性;

聚合是關聯關系的一種特例,他體現的是整體與部分、擁有的關系,即has-a的關系,此時整體與部分之間是可分離的,他們可以具有各自的生命周期,部分可以屬於多個整體對象,也可以為多個整體對象共享;比如計算機與CPU、公司與員工的關系等;表現在代碼層面,和關聯關系是一致的,只能從語義級別來區分;

參考鏈接
10.講講你理解的 nio和 bio 的區別是啥,談談 reactor 模型。
IO(BIO)是面向流的,NIO是面向緩沖區的
BIO:Block IO 同步阻塞式 IO,就是我們平常使用的傳統 IO,它的特點是模式簡單使用方便,並發處理能力低。
NIO:New IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端通過 Channel(通道)通訊,實現了多路復用。
AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了異步非堵塞 IO ,異步 IO 的操作基於事件和回調機制。
參考鏈接1
參考鏈接2
11.反射的原理,反射創建類實例的三種方式是什么
參考鏈接1
參考鏈接2
12.反射中,Class.forName 和 ClassLoader 區別。
參考鏈接
13.描述動態代理的幾種實現方式,分別說出相應的優缺點。
Jdk cglib jdk底層是利用反射機制,需要基於接口方式,這是由於

Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
1
2
Cglib則是基於asm框架,實現了無反射機制進行代理,利用空間來換取了時間,代理效率高於jdk
參考鏈接
動態代理與 cglib 實現的區別
同上(基於invocationHandler和methodInterceptor)
14.為什么 CGlib 方式可以對接口實現代理。
同上
15.final 的用途
類、變量、方法
final 修飾的類叫最終類,該類不能被繼承。
final 修飾的方法不能被重寫。
final 修飾的變量叫常量,常量必須初始化,初始化之后值就不能被修改。
參考鏈接
16.寫出三種單例模式實現。
懶漢式單例,餓漢式單例,雙重檢查等
參考鏈接
17.如何在父類中為子類自動完成所有的 hashcode 和 equals 實現?這么做有何優劣。
同時復寫hashcode和equals方法,優勢可以添加自定義邏輯,且不必調用超類的實現。
參考鏈接
18.請結合 OO 設計理念,談談訪問修飾符 public、private、protected、default 在應用設計中的作用。
訪問修飾符,主要標示修飾塊的作用域,方便隔離防護

類中的數據成員和成員函數據具有的訪問權限包括:public、private、protect、default(包訪問權限)
作用域 當前類 同一package 子孫類 其他package
public √ √ √ √
protected √ √ √ ×
default √ √ × ×
private √ × × ×

public 所有類可見
protected 本包和所有子類都可見(本包中的子類非子類均可訪問,不同包中的子類可以訪問,不是子類不能訪問)
default 本包可見(即默認的形式)(本包中的子類非子類均可訪問,不同包中的類及子類均不能訪問)
priavte 本類可見
1
2
3
4
5
6
7
8
9
10
11
public: Java語言中訪問限制最寬的修飾符,一般稱之為“公共的”。被其修飾的類、屬性以及方法不僅可以跨類訪問,而且允許跨包(package)訪問。
private: Java語言中對訪問權限限制的最窄的修飾符,一般稱之為“私有的”。被其修飾的類、屬性以及方法只能被該類的對象訪問,其子類不能訪問,更不能允許跨包訪問。
protect: 介於public 和 private 之間的一種訪問修飾符,一般稱之為“保護形”。被其修飾的類、屬性以及方法只能被類本身的方法及子類訪問,即使子類在不同的包中也可以訪問。
default:即不加任何訪問修飾符,通常稱為"默認訪問模式"。該模式下,只允許在同一個包中進行訪問。
19.深拷貝和淺拷貝區別。
參考鏈接
20.數組和鏈表數據結構描述,各自的時間復雜度
參考鏈接
21.error 和 exception 的區別,CheckedException,RuntimeException 的區別
參考鏈接
22.請列出 5 個運行時異常。
同上
23.在自己的代碼中,如果創建一個 java.lang.String 對象,這個對象是否可以被類加載器加載?為什么
類加載無須等到“首次使用該類”時加載,jvm允許預加載某些類。。。。
參考鏈接
24.說一說你對 java.lang.Object 對象中 hashCode 和 equals 方法的理解。在什么場景下需要重新實現這兩個方法。
參考上邊試題(17)
25.在 jdk1.5 中,引入了泛型,泛型的存在是用來解決什么問題。
泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數,泛型的好處是在編譯的時候檢查類型安全,並且所有的強制轉換都是自動和隱式的,以提高代碼的重用率
參考鏈接
26.這樣的 a.hashcode() 有什么用,與 a.equals(b)有什么關系。
hashcode
hashcode()方法提供了對象的hashCode值,是一個native方法,返回的默認值與System.identityHashCode(obj)一致。
通常這個值是對象頭部的一部分二進制位組成的數字,具有一定的標識對象的意義存在,但絕不定於地址。
作用是:用一個數字來標識對象。比如在HashMap、HashSet等類似的集合類中,如果用某個對象本身作為Key,即要基於這個對象實現Hash的寫入和查找,那么對象本身如何實現這個呢?就是基於hashcode這樣一個數字來完成的,只有數字才能完成計算和對比操作。
hashcode是否唯一
hashcode只能說是標識對象,在hash算法中可以將對象相對離散開,這樣就可以在查找數據的時候根據這個key快速縮小數據的范圍,但hashcode不一定是唯一的,所以hash算法中定位到具體的鏈表后,需要循環鏈表,然后通過equals方法來對比Key是否是一樣的。
equals與hashcode的關系
equals相等兩個對象,則hashcode一定要相等。但是hashcode相等的兩個對象不一定equals相等。
參考鏈接
27.有沒有可能 2 個不相等的對象有相同的 hashcode。

實例:

String str1 = "通話";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d", str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));

執行結果:str1:1179395 | str2:1179395
false
1
2
3
4
5
6
7
28.Java 中的 HashSet 內部是如何工作的。
底層是基於hashmap實現的
參考鏈接
什么是序列化,怎么序列化,為什么序列化,反序列化會遇到什么問題,如何解決。
參考鏈接
29. JDK 和 JRE 有什么區別?
JDK:Java Development Kit 的簡稱,java 開發工具包,提供了 java 的開發環境和運行環境。
JRE:Java Runtime Environment 的簡稱,java 運行環境,為 java 的運行提供了所需環境。
具體來說 JDK 其實包含了 JRE,同時還包含了編譯 java 源碼的編譯器 javac,還包含了很多 java 程序調試和分析的工具。簡單來說:如果你需要運行 java 程序,只需安裝 JRE 就可以了,如果你需要編寫 java 程序,需要安裝 JDK。
30. == 和 equals 的區別是什么?
== 解讀:
對於基本類型和引用類型 == 的作用效果是不同的,如下所示:
基本類型:比較的是值是否相同;
引用類型:比較的是引用是否相同;
實例:

String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true,引用相同
System.out.println(x==z); // false,==:string比較引用,開辟了新的堆內存空間,所以false
System.out.println(x.equals(y)); // true,equals:string:比較值,相同
System.out.println(x.equals(z)); // true,equals:string比較值,相同
1
2
3
4
5
6
7
equals 解讀:
equals 本質上就是 ==,只不過 String 和 Integer 等重寫了 equals 方法,把它變成了值比較。看下面的代碼就明白了。
首先來看默認情況下 equals 比較一個(有相同值的對象),代碼如下:

public class Cat {
private String name;
public Cat(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
Cat c1 = new Cat("cat1");//c1是Cat的實例化對象,c2同理
Cat c2 = new Cat("cat2");
String s1 = new String("隔壁老王");
String s2 = new String("隔壁老王");
System.out.println(c1.equals(c2));//false,equals在比較的類對象的時候比較的是引用
System.out.println(s1.equals(s2)); //true,而在比較string的時候,因為重寫了equals方法,和基本數據類型一樣,比較的是值,所以為true

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
總結 :== 對於基本類型來說是值比較(不難理解,八種基本數據類型是可以有確定值的),對於引用類型來說是比較的是引用(數組、類、接口沒有確定值);而 equals 默認情況下是引用比較,只是很多類重新了 equals 方法,比如 String、Integer 等把它變成了值比較,所以一般情況下 equals 比較的是值是否相等。
31.java 中的 Math.round(-1.5) 等於多少?
等於 -1,因為在數軸上取值時,中間值(0.5)向右取整,所以正 0.5 是往上取整,負 0.5 是直接舍棄。同理,Math.round(1.5) = 2
32.寫一個字符串反轉函數。

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // gfedcba
1
2
3
4
5
6
7
8
33.String 類的常用方法都有那些?
indexOf():返回指定字符的索引。
charAt():返回指定索引處的字符。
replace():字符串替換。
trim():去除字符串兩端空白。
split():分割字符串,返回一個分割后的字符串數組。
getBytes():返回字符串的 byte 類型數組。
length():返回字符串長度。
toLowerCase():將字符串轉成小寫字母。
toUpperCase():將字符串轉成大寫字符。
substring():截取字符串。
equals():字符串比較。
34.抽象類必須要有抽象方法嗎?
不需要,抽象類不一定非要有抽象方法。
示例代碼:

public abstract class noAbstractMethod{
public static void main(String[] args) {
sayHi();
}
public static void sayHi() {
System.out.println("hi~");
}
}
結果:hi~
1
2
3
4
5
6
7
8
9
35.java 中 IO 流分為幾種?
按功能來分:輸入流(input)、輸出流(output)。

按類型來分:字節流和字符流。

字節流和字符流的區別是:字節流按 8 位傳輸以字節為單位輸入輸出數據,字符流按 16 位傳輸以字符為單位輸入輸出數據。
36.Files的常用方法都有哪些?
Files.exists():檢測文件路徑是否存在。
Files.createFile():創建文件。
Files.createDirectory():創建文件夾。
Files.delete():刪除一個文件或目錄。
Files.copy():復制文件。
Files.move():移動文件。
Files.size():查看文件個數。
Files.read():讀取文件。
Files.write():寫入文件。
37.List、Set、Map 之間的區別是什么?

38.如何實現數組和 List 之間的轉換?
List轉換成為數組:調用ArrayList的toArray方法。
數組轉換成為List:調用Arrays的asList方法。
39. ArrayList 和 Vector 的區別是什么?
Vector是同步的,而ArrayList不是。然而,如果你尋求在迭代的時候對列表進行改變,你應該使用CopyOnWriteArrayList。
ArrayList比Vector快,它是異步,不會過載。
ArrayList更加通用,因為我們可以使用Collections工具類輕易地獲取同步列表和只讀列表。
40.Array 和 ArrayList 有何區別?
Array可以容納基本類型和對象,而ArrayList只能容納對象。
Array是指定大小的,而ArrayList初始大小是固定的。
Array沒有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。
41.在 Queue 中 poll()和 remove()有什么區別?
poll() 和 remove() 都是從隊列中取出一個元素,但是 poll() 在獲取元素失敗的時候會返回空,但是 remove() 失敗的時候會拋出異常。
42. 哪些集合類是線程安全的?
vector:就比arraylist多了個同步化機制(線程安全),因為效率較低,現在已經不太建議使用。在web應用中,特別是前台頁面,往往效率(頁面響應速度)是優先考慮的。
statck:堆棧類,先進后出。
hashtable:就比hashmap多了個線程安全。
enumeration:枚舉,相當於迭代器。
43.迭代器 Iterator 是什么?
迭代器是一種設計模式,它是一個對象,它可以遍歷並選擇序列中的對象,而開發人員不需要了解該序列的底層結構。迭代器通常被稱為“輕量級”對象,因為創建它的代價小。
44.Iterator 怎么使用?有什么特點?
Java中的Iterator功能比較簡單,並且只能單向移動:

(1) 使用方法iterator()要求容器返回一個Iterator。第一次調用Iterator的next()方法時,它返回序列的第一個元素。注意:iterator()方法是java.lang.Iterable接口,公共基類Collection提供iterator()方法。

(2) 使用next()獲得序列中的下一個元素。

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

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

Iterator是Java迭代器最簡單的實現,為List設計的ListIterator具有更多的功能,它可以從兩個方向遍歷List,也可以從List中插入和刪除元素。
45. Iterator 和 ListIterator 有什么區別?
Iterator可用來遍歷Set和List集合,但是ListIterator只能用來遍歷List,見名知意,Set並不能使用ListIterator
Iterator對集合只能是前向遍歷,ListIterator既可以前向也可以后向。
ListIterator實現了Iterator接口,並包含其他的功能,比如:增加元素,替換元素,獲取前一個和后一個元素的索引,等等。
46.synchronized 和 volatile 的區別是什么?
volatile本質是在告訴jvm當前變量在寄存器(工作內存)中的值是不確定的,需要從主存中讀取; synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住。
volatile僅能使用在變量級別;synchronized則可以使用在變量、方法、和類級別的。
volatile僅能實現變量的修改可見性,不能保證原子性;而synchronized則可以保證變量的修改可見性和原子性。
volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。
volatile標記的變量不會被編譯器優化;synchronized標記的變量可以被編譯器優化。
47.給定一個文本,獲取某字符串出現的次數

public static void count(){

File file = new File("C:\\Users\\Administrator\\Desktop\\行政區划配置表.txt");
InputStream is = null;
try {
is = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] b = new byte[1024];
int a = 0;
try {
a = is.read(b);
} catch (IOException e) {
e.printStackTrace();
}
String[] str = new String(b, 0, a).split("");
int count = 0;
for(int i = 0;i<str.length;i++){
//設置查詢統計的字符串為a
if("a".equals(str[i])){
count++;
}
}
System.out.println(count);
}
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
2.JVM 知識
1.什么情況下會發生棧內存溢出。
如果線程請求的棧深度大於虛擬機所允許的深度,將拋出StackOverflowError異常。 如果虛擬機在動態擴展棧時無法申請到足夠的內存空間,則拋出OutOfMemoryError異常。
參考鏈接
2.JVM 的內存結構,Eden 和 Survivor 比例。

eden 和 survior 是按8比1分配的
參考鏈接
3.jvm 中一次完整的 GC 流程是怎樣的,對象如何晉升到老年代,說說你知道的幾種主要的jvm 參數。
對象誕生即新生代->eden,在進行minor gc過程中,如果依舊存活,移動到from,變成Survivor,進行標記代數,如此檢查一定次數后,晉升為老年代,
參考鏈接1
參考鏈接2
參考鏈接3
4.你知道哪幾種垃圾收集器,各自的優缺點,重點講下 cms,包括原理,流程,優缺點
Serial、parNew、ParallelScavenge、SerialOld、ParallelOld、CMS、G1
參考鏈接
5.垃圾回收算法的實現原理。
參考鏈接
6.當出現了內存溢出,你怎么排錯。
首先分析是什么類型的內存溢出,對應的調整參數或者優化代碼。
參考鏈接
7.JVM 內存模型的相關知識了解多少,比如重排序,內存屏障,happen-before,主內存,工作內存等。
內存屏障:為了保障執行順序和可見性的一條cpu指令
重排序:為了提高性能,編譯器和處理器會對執行進行重拍
happen-before:操作間執行的順序關系。有些操作先發生。
主內存:共享變量存儲的區域即是主內存
工作內存:每個線程copy的本地內存,存儲了該線程以讀/寫共享變量的副本
參考鏈接1
參考鏈接2
參考鏈接3
8.簡單說說你了解的類加載器。
類加載器的分類(bootstrap,ext,app,curstom),類加載的流程(load-link-init)
參考鏈接
9.講講 JAVA 的反射機制。
Java程序在運行狀態可以動態的獲取類的所有屬性和方法,並實例化該類,調用方法的功能
參考鏈接
10.你們線上應用的 JVM 參數有哪些。
-server
-Xms6000M
-Xmx6000M
-Xmn500M
-XX:PermSize=500M
-XX:MaxPermSize=500M
-XX:SurvivorRatio=65536
-XX:MaxTenuringThreshold=0
-Xnoclassgc
-XX:+DisableExplicitGC
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0
-XX:+CMSClassUnloadingEnabled
-XX:-CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=90
-XX:SoftRefLRUPolicyMSPerMB=0
-XX:+PrintClassHistogram
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC
-Xloggc:log/gc.log
11.g1 和 cms 區別,吞吐量優先和響應優先的垃圾收集器選擇。
Cms是以獲取最短回收停頓時間為目標的收集器。基於標記-清除算法實現。比較占用cpu資源,切易造成碎片。
G1是面向服務端的垃圾收集器,是jdk9默認的收集器,基於標記-整理算法實現。可利用多核、多cpu,保留分代,實現可預測停頓,可控。
參考鏈接
請解釋如下 jvm 參數的含義:
-server -Xms512m -Xmx512m -Xss1024K
-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20
XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly。
Server模式啟動
最小堆內存512m
最大512m
每個線程棧空間1m
永久代256m
最大永久代512m
最大轉為老年代檢查次數20
Cms回收開啟時機:內存占用80%
命令JVM不基於運行時收集的數據來啟動CMS垃圾收集周期
12.32位系統jvm堆內存如下哪一個設置是最大且有效的( B )
A. -Xmx1000m
B. -Xmx1500m
C. -Xmx8G
D. 無限
JVM最大內存: 首先JVM內存限制於實際的最大物理內存,假設物理內存無限大的話,JVM內存的最大值跟操作系統有很大的關系。簡單的說就32位處理器雖然可控內存空間有4GB,但是具體的操作系統會給一個限制,這個限制一般是2GB-3GB(一般來說Windows系統下為1.5G-2G,Linux系統下為2G-3G),而64bit以上的處理器就不會有限制了。

3.開源框架知識
1.簡單講講 tomcat 結構,以及其類加載器流程。
Server- --多個service
Container級別的:–>engine–》host–>context
Listenter
Connector
Logging、Naming、Session、JMX等等

通過WebappClassLoader 加載class
參考鏈接1
參考鏈接2
參考鏈接3
參考鏈接4
2.tomcat 如何調優,涉及哪些參數。
硬件上選擇,操作系統選擇,版本選擇,jdk選擇,配置jvm參數,配置connector的線程數量,開啟gzip壓縮,trimSpaces,集群等
a) 內存優化:主要是對Tomcat啟動參數進行優化,我們可以在Tomcat啟動腳本中修改它的最大內存數等等。

b) 線程數優化:Tomcat的並發連接參數,主要在Tomcat配置文件中server.xml中配置,比如修改最小空閑連接線程數,用於提高系統處理性能等等。

c) 優化緩存:打開壓縮功能,修改參數,比如壓縮的輸出內容大小默認為2KB,可以適當的修改。
參考鏈接
3.講講 Spring 加載流程。
通過listener入口,核心是在AbstractApplicationContext的refresh方法,在此處進行裝載bean工廠,bean,創建bean實例,攔截器,后置處理器等。
參考鏈接
4.講講 Spring 事務的傳播屬性。
七種傳播屬性。
事務傳播行為
所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。在TransactionDefinition定義中包括了如下幾個表示傳播行為的常量:
①TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
②TransactionDefinition.PROPAGATION_REQUIRES_NEW:創建一個新的事務,如果當前存在事務,則把當前事務掛起。
③TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
④TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
⑤TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
⑥TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
⑦TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
參考鏈接
5.Spring 如何管理事務的。
編程式和聲明式
同上
6.Spring 怎么配置事務(具體說出一些關鍵的 xml 元素)。
配置事務的方法有兩種:
1)、基於XML的事務配置。

<?xml version="1.0" encoding="UTF-8"?>
<!-- from the file 'context.xml' -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<!-- 數據元信息 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>

<!-- 管理事務的類,指定我們用誰來管理我們的事務-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- 首先我們要把服務對象聲明成一個bean 例如HelloService -->
<bean id="helloService" class="com.yintong.service.HelloService"/>

<!-- 然后是聲明一個事物建議tx:advice,spring為我們提供了事物的封裝,這個就是封裝在了<tx:advice/>中 -->
<!-- <tx:advice/>有一個transaction-manager屬性,我們可以用它來指定我們的事物由誰來管理。
默認:事務傳播設置是 REQUIRED,隔離級別是DEFAULT -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 配置這個事務建議的屬性 -->
<tx:attributes>
<!-- 指定所有get開頭的方法執行在只讀事務上下文中 -->
<tx:method name="get*" read-only="true"/>
<!-- 其余方法執行在默認的讀寫上下文中 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

<!-- 我們定義一個切面,它匹配FooService接口定義的所有操作 -->
<aop:config>
<!-- <aop:pointcut/>元素定義AspectJ的切面表示法,這里是表示com.yintong.service.helloService包下的任意方法。 -->
<aop:pointcut id="helloServiceOperation" expression="execution(* com.yintong.service.helloService.*(..))"/>
<!-- 然后我們用一個通知器:<aop:advisor/>把這個切面和tx:advice綁定在一起,表示當這個切面:fooServiceOperation執行時tx:advice定義的通知邏輯將被執行 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="helloServiceOperation"/>
</aop:config>

</beans>
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
2)、基於注解方式的事務配置。
@Transactional:直接在Java源代碼中聲明事務的做法讓事務聲明和將受其影響的代碼距離更近了,而且一般來說不會有不恰當的耦合的風險,因為,使用事務性的代碼幾乎總是被部署在事務環境中。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<bean id="helloService" class="com.yintong.service.HelloService"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置注解事務 -->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
參考鏈接
7.說說你對 Spring 的理解,非單例注入的原理?它的生命周期?循環注入的原理, aop 的實現原理,說說 aop 中的幾個術語,它們是怎么相互工作的。
AOP與IOC的概念(即spring的核心)

a) IOC:Spring是開源框架,使用框架可以使我們減少工作量,提高工作效率並且它是分層結構,即相對應的層處理對應的業務邏輯,減少代碼的耦合度。而spring的核心是IOC控制反轉和AOP面向切面編程。IOC控制反轉主要強調的是程序之間的關系是由容器控制的,容器控制對象,控制了對外部資源的獲取。而反轉即為,在傳統的編程中都是由我們創建對象獲取依賴對象,而在IOC中是容器幫我們創建對象並注入依賴對象,正是容器幫我們查找和注入對象,對象是被獲取,所以叫反轉。

b) AOP:面向切面編程,主要是管理系統層的業務,比如日志,權限,事物等。AOP是將封裝好的對象剖開,找出其中對多個對象產生影響的公共行為,並將其封裝為一個可重用的模塊,這個模塊被命名為切面(aspect),切面將那些與業務邏輯無關,卻被業務模塊共同調用的邏輯提取並封裝起來,減少了系統中的重復代碼,降低了模塊間的耦合度,同時提高了系統的可維護性。

核心組件:bean,context,core,單例注入是通過單例beanFactory進行創建,生命周期是在創建的時候通過接口實現開啟,循環注入是通過后置處理器,aop其實就是通過反射進行動態代理,pointcut,advice等。
Aop相關:
參考鏈接
8.Springmvc 中 DispatcherServlet 初始化過程。
入口是web.xml中配置的ds,ds繼承了HttpServletBean,FrameworkServlet,通過其中的init方法進行初始化裝載bean和實例,initServletBean是實際完成上下文工作和bean初始化的方法。
參考鏈接
9.springMVC的執行流程
springMVC是由dispatchservlet為核心的分層控制框架。首先客戶端發出一個請求web服務器解析請求url並去匹配dispatchservlet的映射url,如果匹配上就將這個請求放入到dispatchservlet,dispatchservlet根據mapping映射配置去尋找相對應的handel,然后把處理權交給找到的handel,handel封裝了處理業務邏輯的代碼,當handel處理完后會返回一個邏輯視圖modelandview給dispatchservlet,此時的modelandview是一個邏輯視圖不是一個正式視圖,所以dispatchservlet會通過viewresource視圖資源去解析modelandview,然后將解析后的參數放到view中返回到客戶端並展現。
10.事物的理解

a) 事物具有原子性,一致性,持久性,隔離性
b) 原子性:是指在一個事物中,要么全部執行成功,要么全部失敗回滾。
c) 一致性:事物執行之前和執行之后都處於一致性狀態
d) 持久性:事物多數據的操作是永久性
e) 隔離性:當一個事物正在對數據進行操作時,另一個事物不可以對數據進行操作,也就是多個並發事物之間相互隔離。
1
2
3
4
5
4.操作系統
1.Linux 系統下你關注過哪些內核參數,說說你知道的。

Tcp/ip io cpu memory
net.ipv4.tcp_syncookies = 1
#啟用syncookies
net.ipv4.tcp_max_syn_backlog = 8192
#SYN隊列長度
net.ipv4.tcp_synack_retries=2
#SYN ACK重試次數
net.ipv4.tcp_fin_timeout = 30
#主動關閉方FIN-WAIT-2超時時間
net.ipv4.tcp_keepalive_time = 1200
#TCP發送keepalive消息的頻度
net.ipv4.tcp_tw_reuse = 1
#開啟TIME-WAIT重用
net.ipv4.tcp_tw_recycle = 1
#開啟TIME-WAIT快速回收
net.ipv4.ip_local_port_range = 1024 65000
#向外連接的端口范圍
net.ipv4.tcp_max_tw_buckets = 5000
#最大TIME-WAIT數量,超過立即清除
net.ipv4.tcp_syn_retries = 2
#SYN重試次數
echo “fs.file-max=65535” >> /etc/sysctl.conf
sysctl -p
參考鏈接
2.Linux 下 IO 模型有幾種,各自的含義是什么。
阻塞式io,非阻塞io,io復用模型,信號驅動io模型,異步io模型。
參考鏈接1
參考鏈接2
3.epoll 和 poll 有什么區別。
select的本質是采用32個整數的32位,即32* 32= 1024來標識,fd值為1-1024。當fd的值超過1024限制時,就必須修改FD_SETSIZE的大小。這個時候就可以標識32* max值范圍的fd。
對於單進程多線程,每個線程處理多個fd的情況,select是不適合的。
1.所有的線程均是從1-32*max進行掃描,每個線程處理的均是一段fd值,這樣做有點浪費
2.1024上限問題,一個處理多個用戶的進程,fd值遠遠大1024
所以這個時候應該采用poll,
poll傳遞的是數組頭指針和該數組的長度,只要數組的長度不是很長,性能還是很不錯的,因為poll一次在內核中申請4K(一個頁的大小來存放fd),盡量控制在4K以內,
epoll還是poll的一種優化,返回后不需要對所有的fd進行遍歷,在內核中維持了fd的列表。select和poll是將這個內核列表維持在用戶態,然后傳遞到內核中。但是只有在2.6的內核才支持。
epoll更適合於處理大量的fd ,且活躍fd不是很多的情況,畢竟fd較多還是一個串行的操作
參考鏈接
4.平時用到哪些 Linux 命令。
Ls,find,tar,tail,cp,rm,vi,grep,ps,pkill等等
參考鏈接
5.用一行命令查看文件的最后五行。
Tail -n 5 filename
6.用一行命令輸出正在運行的 java 進程。
ps -ef|grep Java
7.介紹下你理解的操作系統中線程切換過程。
控制權的轉換,根據優先級切換上下文(用戶,寄存器,系統)
參考鏈接
8.進程和線程的區別。
Linux 實現並沒有區分這兩個概念(進程和線程)
進程:程序的一次執行
線程:CPU的基本調度單位
一個進程可以包含多個線程。
參考鏈接

5.多線程
1.多線程的幾種實現方式,什么是線程安全。
實現runable接口,繼承thread類。
參考鏈接
2.volatile 的原理,作用,能代替鎖么。
Volatile利用內存柵欄機制來保持變量的一致性。不能代替鎖,其只具備數據可見性一致性,不具備原子性。
參考鏈接
3.畫一個線程的生命周期狀態圖。
新建,可運行,運行中,睡眠,阻塞,等待,死亡。

參考鏈接
4.sleep 和 wait 的區別。
Sleep是休眠線程,wait是等待,sleep是thread的靜態方法,wait則是object的方法。
Sleep依舊持有鎖,並在指定時間自動喚醒。wait則釋放鎖。
參考鏈接
5.Lock 與 Synchronized 的區別。
首先兩者都保持了並發場景下的原子性和可見性,區別則是synchronized的釋放鎖機制是交由其自身控制,且互斥性在某些場景下不符合邏輯,無法進行干預,不可人為中斷等。
而lock常用的則有ReentrantLock和readwritelock兩者,添加了類似鎖投票、定時鎖等候和可中斷鎖等候的一些特性。此外,它還提供了在激烈爭用情況下更佳的性能。
參考鏈接
6.synchronized 的原理是什么,解釋以下名詞:重排序,自旋鎖,偏向鎖,輕量級鎖,可重入鎖,公平鎖,非公平鎖,樂觀鎖,悲觀鎖。
Synchronized底層是通過監視器的enter和exit實現
參考鏈接1
參考鏈接2
7.用過哪些原子類,他們的原理是什么。
AtomicInteger; AtomicLong; AtomicReference; AtomicBoolean;基於CAS原語實現 ,比較並交換、加載鏈接/條件存儲,最壞的情況下是旋轉鎖
參考鏈接1
參考鏈接2
8.用過線程池嗎,newCache 和 newFixed 有什么區別,他們的原理簡單概括下,構造函數的各個參數的含義是什么,比如 coreSize,maxsize 等。
newSingleThreadExecutor返回以個包含單線程的Executor,將多個任務交給此Exector時,這個線程處理完一個任務后接着處理下一個任務,若該線程出現異常,將會有一個新的線程來替代。
newFixedThreadPool返回一個包含指定數目線程的線程池,如果任務數量多於線程數目,那么沒有沒有執行的任務必須等待,直到有任務完成為止。
newCachedThreadPool根據用戶的任務數創建相應的線程來處理,該線程池不會對線程數目加以限制,完全依賴於JVM能創建線程的數量,可能引起內存不足。
底層是基於ThreadPoolExecutor實現,借助reentrantlock保證並發。
coreSize核心線程數,maxsize最大線程數。
參考鏈接
9.線程池的關閉方式有幾種,各自的區別是什么。
Shutdown shutdownNow tryTerminate 清空工作隊列,終止線程池中各個線程,銷毀線程池
參考鏈接
10.假如有一個第三方接口,有很多個線程去調用獲取數據,現在規定每秒鍾最多有 10 個線程同時調用它,如何做到。
ScheduledThreadPoolExecutor 設置定時,進行調度。

public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
1
2
3
4
5
參考鏈接
11.spring 的 controller 是單例還是多例,怎么保證並發的安全。
單例
通過單例工廠 DefaultSingletonBeanRegistry實現單例
通過AsyncTaskExecutor保持安全
12.用三個線程按順序循環打印 abc 三個字母,比如 abcabcabc。

public class ABC_Synch {
public static class ThreadPrinter implements Runnable {
private String name;
private Object prev;
private Object self;
private ThreadPrinter(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {// 多線程並發,不能用if,必須使用whil循環
synchronized (prev) { // 先獲取 prev 鎖
synchronized (self) {// 再獲取 self 鎖
System.out.print(name);//打印
count--;

self.notifyAll();// 喚醒其他線程競爭self鎖,注意此時self鎖並未立即釋放。
}
//此時執行完self的同步塊,這時self鎖才釋放。
try {
prev.wait(); // 立即釋放 prev鎖,當前線程休眠,等待喚醒
/**
* JVM會在wait()對象鎖的線程中隨機選取一線程,賦予其對象鎖,喚醒線程,繼續執行。
*/
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
ThreadPrinter pa = new ThreadPrinter("A", c, a);
ThreadPrinter pb = new ThreadPrinter("B", a, b);
ThreadPrinter pc = new ThreadPrinter("C", b, c);

new Thread(pa).start();
Thread.sleep(10);//保證初始ABC的啟動順序
new Thread(pb).start();
Thread.sleep(10);
new Thread(pc).start();
Thread.sleep(10);
}
}

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
參考鏈接
13.ThreadLocal 用過么,用途是什么,原理是什么,用的時候要注意什么。
Threadlocal底層是通過threadlocalMap進行存儲鍵值 每個ThreadLocal類創建一個Map,然后用線程的ID作為Map的key,實例對象作為Map的value,這樣就能達到各個線程的值隔離的效果。
ThreadLocal的作用是提供線程內的局部變量,這種變量在線程的生命周期內起作用,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的復雜度。
誰設置誰負責移除
參考鏈接1
參考鏈接2
14.如果讓你實現一個並發安全的鏈表,你會怎么做。
Collections.synchronizedList() ConcurrentLinkedQueue
參考鏈接
15.有哪些無鎖數據結構,他們實現的原理是什么。
LockFree,CAS
基於jdk提供的原子類原語實現,例如AtomicReference
參考鏈接
16.講講 java 同步機制的 wait 和 notify。
首先這兩個方法只能在同步代碼塊中調用,wait會釋放掉對象鎖,等待notify喚醒。
參考鏈接
17.多線程如果線程掛住了怎么辦。
根據具體情況(sleep,wait,join等),酌情選擇notifyAll,notify進行線程喚醒。
參考鏈接
18.countdowlatch 和 cyclicbarrier 的內部原理和用法,以及相互之間的差別。
CountDownLatch是一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它運行一個或者多個線程一直處於等待狀態。
CyclicBarrier要做的事情是,讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,直到最后一個線程到達屏障時,屏障才會開門,所有被屏障攔截的線程才會繼續運行。
CyclicBarrier初始化的時候,設置一個屏障數。線程調用await()方法的時候,這個線程就會被阻塞,當調用await()的線程數量到達屏障數的時候,主線程就會取消所有被阻塞線程的狀態。
前者是遞減,不可循環,后者是遞加,可循環用
countdowlatch 基於abq cb基於ReentrantLock Condition
參考鏈接1
參考鏈接2
19.使用 synchronized 修飾靜態方法和非靜態方法有什么區別。
對象鎖和類鎖
參考鏈接
20.簡述 ConcurrentLinkedQueue LinkedBlockingQueue 的用處和不同之處。
LinkedBlockingQueue 是一個基於單向鏈表的、范圍任意的(其實是有界的)、FIFO 阻塞隊列。
ConcurrentLinkedQueue是一個基於鏈接節點的無界線程安全隊列,它采用先進先出的規則對節點進行排序,當我們添加一個元素的時候,它會添加到隊列的尾部,當我們獲取一個元素時,它會返回隊列頭部的元素。它采用了“wait-free”算法來實現,該算法在Michael & Scott算法上進行了一些修改, Michael & Scott算法的詳細信息可以參見參考資料一。
參考鏈接1
參考鏈接2
參考鏈接3
##導致線程死鎖的原因?怎么解除線程死鎖。
死鎖問題是多線程特有的問題,它可以被認為是線程間切換消耗系統性能的一種極端情況。在死鎖時,線程間相互等待資源,而又不釋放自身的資源,導致無窮無盡的等待,其結果是系統任務永遠無法執行完成。死鎖問題是在多線程開發中應該堅決避免和杜絕的問題。
一般來說,要出現死鎖問題需要滿足以下條件:

互斥條件:一個資源每次只能被一個線程使用。
請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
不剝奪條件:進程已獲得的資源,在未使用完之前,不能強行剝奪。
循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。
只要破壞死鎖 4 個必要條件之一中的任何一個,死鎖問題就能被解決。
1
2
3
4
5
參考鏈接
21.非常多個線程(可能是不同機器),相互之間需要等待協調,才能完成某種工作,問怎么設計這種協調方案。
此問題的本質是保持順序執行。可以使用executors
#TCP 與 HTTP
22.http1.0 和 http1.1 有什么區別。
HTTP 1.0主要有以下幾點變化:
請求和相應可以由於多行首部字段構成
響應對象前面添加了一個響應狀態行
響應對象不局限於超文本
服務器與客戶端之間的連接在每次請求之后都會關閉
實現了Expires等傳輸內容的緩存控制
內容編碼Accept-Encoding、字符集Accept-Charset等協商內容的支持
這時候開始有了請求及返回首部的概念,開始傳輸不限於文本(其他二進制內容)
HTTP 1.1加入了很多重要的性能優化:持久連接、分塊編碼傳輸、字節范圍請求、增強的緩存機制、傳輸編碼及請求管道。
參考鏈接
23.TCP 三次握手和四次揮手的流程,為什么斷開連接要 4 次,如果握手只有兩次,會出現什么。

第一次握手(SYN=1, seq=x):
客戶端發送一個 TCP 的 SYN 標志位置1的包,指明客戶端打算連接的服務器的端口,以及初始序號 X,保存在包頭的序列號(Sequence Number)字段里。
發送完畢后,客戶端進入 <code>SYN_SEND</code> 狀態。

第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
服務器發回確認包(ACK)應答。即 SYN 標志位和 ACK 標志位均為1。服務器端選擇自己 ISN 序列號,放到 Seq 域里,同時將確認序號(Acknowledgement Number)設置為客戶的 ISN 加1,即X+1。
發送完畢后,服務器端進入 <code>SYN_RCVD</code> 狀態。

第三次握手(ACK=1,ACKnum=y+1)
客戶端再次發送確認包(ACK),SYN 標志位為0,ACK 標志位為1,並且把服務器發來 ACK 的序號字段+1,放在確定字段中發送給對方,並且在數據段放寫ISN的+1
1
2
3
4
5
6
7
8
9
10
發送完畢后,客戶端進入 ESTABLISHED 狀態,當服務器端接收到這個包時,也進入 ESTABLISHED 狀態,TCP 握手結束。
第一次揮手(FIN=1,seq=x)
假設客戶端想要關閉連接,客戶端發送一個 FIN 標志位置為1的包,表示自己已經沒有數據可以發送了,但是仍然可以接受數據。
發送完畢后,客戶端進入 FIN_WAIT_1 狀態。
第二次揮手(ACK=1,ACKnum=x+1)
服務器端確認客戶端的 FIN 包,發送一個確認包,表明自己接受到了客戶端關閉連接的請求,但還沒有准備好關閉連接。
發送完畢后,服務器端進入 CLOSE_WAIT 狀態,客戶端接收到這個確認包之后,進入 FIN_WAIT_2 狀態,等待服務器端關閉連接。
第三次揮手(FIN=1,seq=y)
服務器端准備好關閉連接時,向客戶端發送結束連接請求,FIN 置為1。
發送完畢后,服務器端進入 LAST_ACK 狀態,等待來自客戶端的最后一個ACK。
第四次揮手(ACK=1,ACKnum=y+1)
客戶端接收到來自服務器端的關閉請求,發送一個確認包,並進入 TIME_WAIT狀態,等待可能出現的要求重傳的 ACK 包。
服務器端接收到這個確認包之后,關閉連接,進入 CLOSED 狀態。
客戶端等待了某個固定時間(兩個最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,沒有收到服務器端的 ACK ,認為服務器端已經正常關閉連接,於是自己也關閉連接,進入 CLOSED 狀態。
兩次后會重傳直到超時。如果多了會有大量半鏈接阻塞隊列。
參考鏈接1
參考鏈接2
24.TIME_WAIT 和 CLOSE_WAIT 的區別。
TIME_WAIT狀態就是用來重發可能丟失的ACK報文。
TIME_WAIT 表示主動關閉,CLOSE_WAIT 表示被動關閉。
25.說說你知道的幾種 HTTP 響應碼,比如 200, 302, 404。

1xx:信息,請求收到,繼續處理
2xx:成功,行為被成功地接受、理解和采納
3xx:重定向,為了完成請求,必須進一步執行的動作
4xx:客戶端錯誤,請求包含語法錯誤或者請求無法實現
5xx:服務器錯誤,服務器不能實現一種明顯無效的請求
200 ok 一切正常
302 Moved Temporatily 文件臨時移出
404 not found
1
2
3
4
5
6
7
8
參考鏈接
26.當你用瀏覽器打開一個鏈接的時候,計算機做了哪些工作步驟。
Dns解析–>端口分析–>tcp請求–>服務器處理請求–>服務器響應–>瀏覽器解析—>鏈接關閉
27.TCP/IP 如何保證可靠性,說說 TCP 頭的結構。
使用序號,對收到的TCP報文段進行排序以及檢測重復的數據;使用校驗和來檢測報文段的錯誤;使用確認和計時器來檢測和糾正丟包或延時。//TCP頭部,總長度20字節

typedef struct _tcp_hdr
{
unsigned short src_port; //源端口號
unsigned short dst_port; //目的端口號
unsigned int seq_no; //序列號
unsigned int ack_no; //確認號
#if LITTLE_ENDIAN
unsigned char reserved_1:4; //保留6位中的4位首部長度
unsigned char thl:4; //tcp頭部長度
unsigned char flag:6; //6位標志
unsigned char reseverd_2:2; //保留6位中的2位
#else
unsigned char thl:4; //tcp頭部長度
unsigned char reserved_1:4; //保留6位中的4位首部長度
unsigned char reseverd_2:2; //保留6位中的2位
unsigned char flag:6; //6位標志
#endif
unsigned short wnd_size; //16位窗口大小
unsigned short chk_sum; //16位TCP檢驗和
unsigned short urgt_p; //16為緊急指針
}
tcp_hdr;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
參考鏈接
28.如何避免瀏覽器緩存。
無法被瀏覽器緩存的請求:
HTTP信息頭中包含Cache-Control:no-cache,pragma:no-cache,或Cache-Control:max-age=0等告訴瀏覽器不用緩存的請求
需要根據Cookie,認證信息等決定輸入內容的動態請求是不能被緩存的
經過HTTPS安全加密的請求(有人也經過測試發現,ie其實在頭部加入Cache-Control:max-age信息,firefox在頭部加入Cache-Control:Public之后,能夠對HTTPS的資源進行緩存,參考《HTTPS的七個誤解》)
POST請求無法被緩存
HTTP響應頭中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的請求無法被緩存
參考鏈接
29.簡述 Http 請求 get 和 post 的區別以及數據包格式。


參考鏈接1
參考鏈接2
30.簡述 HTTP 請求的報文格式。
參考上面
31.HTTPS 的加密方式是什么,講講整個加密解密流程。
加密方式是tls/ssl,底層是通過對稱算法,非對稱,hash算法實現
客戶端發起HTTPS請求 --》2. 服務端的配置 --》
3. 傳送證書 —》4. 客戶端解析證書 5. 傳送加密信息 6. 服務段解密信息 7. 傳輸加密后的信息 8. 客戶端解密信息
參考鏈接
#架構設計與分布式
32.常見的緩存策略有哪些,你們項目中用到了什么緩存系統,如何設計的。
Cdn緩存,redis緩存,ehcache緩存等
Cdn 圖片資源 js等, redis一主一從 echcache緩存數據
33.用 java 自己實現一個 LRU。

final int cacheSize = 100;
Map&lt;String, String&gt; map = new LinkedHashMap&lt;String, String&gt;((int) Math.ceil(cacheSize / 0.75f) + 1, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry&lt;String, String&gt; eldest) {
return size() &gt; cacheSize;
}
};
1
2
3
4
5
6
7
參考鏈接
34.分布式集群下如何做到唯一序列號。
Redis生成,mongodb的objectId,zk生成
參考鏈接
35.設計一個秒殺系統,30 分鍾沒付款就自動關閉交易。
分流 – 限流–異步–公平性(只能參加一次)–用戶體驗(第幾位,多少分鍾,一搶完)
容錯處理
Redis 隊列 mysql
30分鍾關閉 可以借助redis的發布訂閱機制 在失效時進行后續操作,其他mq也可以
參考鏈接
36.如何使用 redis 和 zookeeper 實現分布式鎖?有什么區別優缺點,分別適用什么場景。
首先分布式鎖實現常見的有數據庫鎖(表記錄),緩存鎖,基於zk(臨時有序節點可以實現的)的三種
Redis適用於對性能要求特別高的場景。redis可以每秒執行10w次,內網延遲不超過1ms
缺點是數據存放於內存,宕機后鎖丟失。
鎖無法釋放?使用Zookeeper可以有效的解決鎖無法釋放的問題,因為在創建鎖的時候,客戶端會在ZK中創建一個臨時節點,一旦客戶端獲取到鎖之后突然掛掉(Session連接斷開),那么這個臨時節點就會自動刪除掉。其他客戶端就可以再次獲得鎖。
非阻塞鎖?使用Zookeeper可以實現阻塞的鎖,客戶端可以通過在ZK中創建順序節點,並且在節點上綁定監聽器,一旦節點有變化,Zookeeper會通知客戶端,客戶端可以檢查自己創建的節點是不是當前所有節點中序號最小的,如果是,那么自己就獲取到鎖,便可以執行業務邏輯了。
不可重入?使用Zookeeper也可以有效的解決不可重入的問題,客戶端在創建節點的時候,把當前客戶端的主機信息和線程信息直接寫入到節點中,下次想要獲取鎖的時候和當前最小的節點中的數據比對一下就可以了。如果和自己的信息一樣,那么自己直接獲取到鎖,如果不一樣就再創建一個臨時的順序節點,參與排隊。
單點問題?使用Zookeeper可以有效的解決單點問題,ZK是集群部署的,只要集群中有半數以上的機器存活,就可以對外提供服務。
參考鏈接
37.如果有人惡意創建非法連接,怎么解決。
可以使用filter過濾處理
38.分布式事務的原理,優缺點,如何使用分布式事務。
Two Phase commit協議
優點是可以管理多機事務,擁有無線擴展性 確定是易用性難,承擔延時風險
JTA,atomiks等
參考鏈接
39.什么是一致性 hash。
一致性hash是一種分布式hash實現算法。滿足平衡性 單調性 分散性 和負載。
參考鏈接
40.什么是 restful,講講你理解的 restful。
REST 指的是一組架構約束條件和原則。滿足這些約束條件和原則的應用程序或設計就是 RESTful。
參考鏈接
41.如何設計建立和保持 100w 的長連接。
服務器內核調優(tcp,文件數),客戶端調優,框架選擇(netty)
42.如何防止緩存雪崩。
緩存雪崩可能是因為數據未加載到緩存中,或者緩存同一時間大面積的失效,從而導致所有請求都去查數據庫,導致數據庫CPU和內存負載過高,甚至宕機。
解決思路:
1,采用加鎖計數,或者使用合理的隊列數量來避免緩存失效時對數據庫造成太大的壓力。這種辦法雖然能緩解數據庫的壓力,但是同時又降低了系統的吞吐量。
2,分析用戶行為,盡量讓失效時間點均勻分布。避免緩存雪崩的出現。
3,如果是因為某台緩存服務器宕機,可以考慮做主備,比如:redis主備,但是雙緩存涉及到更新事務的問題,update可能讀到臟數據,需要好好解決。
參考鏈接
43.解釋什么是 MESI 協議(緩存一致性)。
MESI是四種緩存段狀態的首字母縮寫,任何多核系統中的緩存段都處於這四種狀態之一。我將以相反的順序逐個講解,因為這個順序更合理:
失效(Invalid)緩存段,要么已經不在緩存中,要么它的內容已經過時。為了達到緩存的目的,這種狀態的段將會被忽略。一旦緩存段被標記為失效,那效果就等同於它從來沒被加載到緩存中。
共享(Shared)緩存段,它是和主內存內容保持一致的一份拷貝,在這種狀態下的緩存段只能被讀取,不能被寫入。多組緩存可以同時擁有針對同一內存地址的共享緩存段,這就是名稱的由來。
獨占(Exclusive)緩存段,和S狀態一樣,也是和主內存內容保持一致的一份拷貝。區別在於,如果一個處理器持有了某個E狀態的緩存段,那其他處理器就不能同時持有它,所以叫“獨占”。這意味着,如果其他處理器原本也持有同一緩存段,那么它會馬上變成“失效”狀態。
已修改(Modified)緩存段,屬於臟段,它們已經被所屬的處理器修改了。如果一個段處於已修改狀態,那么它在其他處理器緩存中的拷貝馬上會變成失效狀態,這個規律和E狀態一樣。此外,已修改緩存段如果被丟棄或標記為失效,那么先要把它的內容回寫到內存中——這和回寫模式下常規的臟段處理方式一樣。
44.說說你知道的幾種 HASH 算法,簡單的也可以。
哈希(Hash)算法,即散列函數。 它是一種單向密碼體制,即它是一個從明文到密文的不可逆的映射,只有加密過程,沒有解密過程。 同時,哈希函數可以將任意長度的輸入經過變化以后得到固定長度的輸出
MD4 MD5 SHA
參考鏈接
45.什么是 paxos 算法。
Paxos算法是萊斯利·蘭伯特(Leslie Lamport,就是 LaTeX 中的"La",此人現在在微軟研究院)於1990年提出的一種基於消息傳遞的一致性算法。
參考鏈接
##什么是 zab 協議。
ZAB 是 Zookeeper 原子廣播協議的簡稱
整個ZAB協議主要包括消息廣播和崩潰恢復兩個過程,進一步可以分為三個階段,分別是:
發現 Discovery
同步 Synchronization
廣播 Broadcast
組成ZAB協議的每一個分布式進程,都會循環執行這三個階段,將這樣一個循環稱為一個主進程周期。
參考鏈接
##一個在線文檔系統,文檔可以被編輯,如何防止多人同時對同一份文檔進行編輯更新。
點擊編輯的時候,利用redis進行加鎖setNX完了之后 expire 一下
也可以用版本號進行控制
46.線上系統突然變得異常緩慢,你如何查找問題。
逐級排查(網絡,磁盤,內存,cpu),數據庫,日志,中間件等也可通過監控工具排查。
47.說說你平時用到的設計模式。
單例, 代理,模板,策略,命令
a) 單例模式:單例模式核心只需要new一個實例對象的模式,比如數據庫連接,在線人數等,一些網站上看到的在線人數統計就是通過單例模式實現的,把一個計時器存放在數據庫或者內存中,當有人登陸的時候取出來加一再放回去,有人退出登陸的時候取出來減一再放回去,但是當有兩個人同時登陸的時候,會同時取出計數器,同時加一,同時放回去,這樣的話數據就會錯誤,所以需要一個全局變量的對象給全部人使用,只需要new出一個實例對象,這就是單例模式的應用,並且單例模式節省資源,因為它控制了實例對象的個數,並有利於gc回收。

b) 策略模式:就是將幾個類中公共的方法提取到一個新的類中,從而使擴展更容易,保證代碼的可移植性,可維護性強。比如有個需求是寫鴨子對象,鴨子有叫,飛,外形這三種方法,如果每個鴨子類都寫這三個方法會出現代碼的冗余,這時候我們可以把鴨子中的叫,飛,外形這三個方法提取出來,放到鴨父類中,讓每個鴨子都繼承這個鴨父類,重寫這三個方法,這樣封裝的代碼可移植性強,當用戶提出新的需求比如鴨子會游泳,那么對於我們oo程序員來講就非常簡單了我們只需要在鴨父類中加一個游泳的方法,讓會游泳的鴨子重寫游泳方法就可以了。

c) 工廠模式:簡單的工廠模式主要是統一提供實例對象的引用,通過工廠模式接口獲取實例對象的引用。比如一個登陸功能,后端有三個類,controller類,interface類,實現接口的實現類。當客戶端發出一個請求,當請求傳到controller類中時,controller獲取接口的引用對象,而實現接口的實現類中封裝好了登陸的業務邏輯代碼。當你需要加一個注冊需求的時候只需要在接口類中加一個注冊方法,實現類中實現方法,controller獲取接口的引用對象即可,不需要改動原來的代碼,這種做法是的可拓展性強。
參考鏈接
48.Dubbo 的原理,數據怎么流轉的,怎么實現集群,負載均衡,服務注冊和發現。重試轉發,快速失敗的策略是怎樣的。
Dubbo[]是一個分布式服務框架,致力於提供高性能和透明化的RPC遠程服務調用方案,以及SOA服務治理方案。
##Cluster 實現集群
在集群負載均衡時,Dubbo提供了多種均衡策略,缺省為random隨機調用。
Random LoadBalance:隨機,按權重比率設置隨機概率。
RoundRobin LoadBalance:輪循,按公約后的權重比率設置輪循比率。
LeastActive LoadBalance:最少活躍調用數,相同活躍數的隨機,活躍數指調用前后計數差。使慢的提供者收到更少請求,因為越慢的提供者的調用前后計數差會越大。
ConsistentHash LoadBalance:一致性Hash,相同參數的請求總是發到同一提供者。當某一台提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。
快速失敗,只發起一次調用,失敗立即報錯。
參考鏈接
49.一次 RPC 請求的流程是什么。
1)服務消費方(client)調用以本地調用方式調用服務;
2)client stub接收到調用后負責將方法、參數等組裝成能夠進行網絡傳輸的消息體;
3)client stub找到服務地址,並將消息發送到服務端;
4)server stub收到消息后進行解碼;
5)server stub根據解碼結果調用本地的服務;
6)本地服務執行並將結果返回給server stub;
7)server stub將返回結果打包成消息並發送至消費方;
8)client stub接收到消息,並進行解碼;
9)服務消費方得到最終結果。
50.異步模式的用途和意義。
異步模式使用與服務器多核,並發嚴重的場景
可提高服務吞吐量大,不容易受到沖擊,可以采用並發策略,提高響應時間
緩存數據過期后的更新如何設計。
失效:應用程序先從cache取數據,沒有得到,則從數據庫中取數據,成功后,放到緩存中。
命中:應用程序從cache中取數據,取到后返回。
更新:先把數據存到數據庫中,成功后,再讓緩存失效。
51.編程中自己都怎么考慮一些設計原則的,比如開閉原則,以及在工作中的應用。
開閉原則(Open Close Principle)
一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。
里氏代換原則(Liskov Substitution Principle)
子類型必須能夠替換掉它們的父類型。
依賴倒轉原則(Dependence Inversion Principle)
高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。即針對接口編程,不要針對實現編程
接口隔離原則(Interface Segregation Principle)
建立單一接口,不要建立龐大臃腫的接口,盡量細化接口,接口中的方法盡量少
組合/聚合復用原則
說要盡量的使用合成和聚合,而不是繼承關系達到復用的目的
迪米特法則(Law Of Demeter)
迪米特法則其根本思想,是強調了類之間的松耦合,類之間的耦合越弱,越有利於復用,一個處在弱耦合的類被修改,不會對有關系的類造成影響,也就是說,信息的隱藏促進了軟件的復用。
單一職責原則(Single Responsibility Principle)
一個類只負責一項職責,應該僅有一個引起它變化的原因
參考鏈接
52.設計一個社交網站中的“私信”功能,要求高並發、可擴展等等。 畫一下架構圖。
MVC 模式,即常見的 MVC 框架。
SSM SSH SSI等
53.曾經參與設計的服務器架構。
54.應用服務器怎么監控性能,各種方式的區別。
55.如何設計一套高並發支付方案,架構如何設計。
56.如何實現負載均衡,有哪些算法可以實現。
57.Zookeeper 的用途,選舉的原理是什么。
58.Mybatis 的底層實現原理。
59.請思考一個方案,設計一個可以控制緩存總體大小的自動適應的本地緩存。
##請思考一個方案,實現分布式環境下的 countDownLatch。
60.后台系統怎么防止請求重復提交。
可以通過token值進行防止重復提交,存放到redis中,在表單初始化的時候隱藏在表單中,添加的時候在移除。判斷這個狀態即可防止重復提交。
如何看待緩存的使用(本地緩存,集中式緩存),簡述本地緩存和集中式緩存和優缺點。本地緩存在並發使用時的注意事項。
61.描述一個服務從發布到被消費的詳細過程。
##講講你理解的服務治理。
62.如何做到接口的冪等性。
#算法
63.10 億個數字里里面找最小的 10 個。
##有 1 億個數字,其中有 2 個是重復的,快速找到它,時間和空間要最優。
64.2 億個隨機生成的無序整數,找出中間大小的值。
65.給一個不知道長度的(可能很大)輸入字符串,設計一種方案,將重復的字符排重。
66.遍歷二叉樹。
67.有 3n+1 個數字,其中 3n 個中是重復的,只有 1 個是不重復的,怎么找出來。
##常用的排序算法,快排,歸並、冒泡。 快排的最優時間復雜度,最差復雜度。冒泡排序的優化方案。
##二分查找的時間復雜度,優勢。
##一個已經構建好的 TreeSet,怎么完成倒排序。

//冒泡
public static void mp(int a[]) {

int swap = 0;
for (int i = 0; i < a.length; i++) {
for (int j = i; j < a.length; j++) {
if (a[j] > a[i]) {
swap = a[i];
a[i] = a[j];
a[j] = swap;
}
}
}
System.out.println(Arrays.toString(a));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 不使用遞歸的二分查找
*title:commonBinarySearch
*@param arr
*@param key
*@return 關鍵字位置
*/
public static int commonBinarySearch(int[] arr,int key){
int low = 0;
int high = arr.length - 1;
int middle = 0;//定義middle

if(key < arr[low] || key > arr[high] || low > high){
return -1;
}
while(low <= high){
middle = (low + high) / 2;
if(arr[middle] > key){
//比關鍵字大則關鍵字在左區域
high = middle - 1;
}else if(arr[middle] < key){
//比關鍵字小則關鍵字在右區域
low = middle + 1;
}else{
return middle;
}
}
return -1;//最后仍然沒有找到,則返回-1
}

/**
* 使用遞歸的二分查找
*title:recursionBinarySearch
*@param arr 有序數組
*@param key 待查找關鍵字
*@return 找到的位置
*/
public static int recursionBinarySearch(int[] arr,int key,int low,int high){

if(key < arr[low] || key > arr[high] || low > high){
return -1;
}
int middle = (low + high) / 2;//初始中間位置
if(arr[middle] > key){
//比關鍵字大則關鍵字在左區域
return recursionBinarySearch(arr, key, low, middle - 1);
}else if(arr[middle] < key){
//比關鍵字小則關鍵字在右區域
return recursionBinarySearch(arr, key, middle + 1, high);
}else {
return middle;
}
}
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
68.什么是 B+樹,B-樹,列出實際的使用場景。
69.並行和並發有什么區別?
並發當有多個線程在操作時,如果系統只有一個CPU,則它根本不可能真正同時進行一個以上的線程,它只能把CPU運行時間划分成若干個時間段,再將時間 段分配給各個線程執行,在一個時間段的線程代碼運行時,其它線程處於掛起狀。.這種方式我們稱之為並發(Concurrent)。
並行:當系統有一個以上CPU時,則線程的操作有可能非並發。當一個CPU執行一個線程時,另一個CPU可以執行另一個線程,兩個線程互不搶占CPU資源,可以同時進行,這種方式我們稱之為並行(Parallel)。
參考鏈接
70.線程和進程的區別?
簡而言之,進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程。進程在執行過程中擁有獨立的內存單元,而多個線程共享內存資源,減少切換次數,從而效率更高。線程是進程的一個實體,是cpu調度和分派的基本單位,是比程序更小的能獨立運行的基本單位。同一進程中的多個線程之間可以並發執行。
71.守護線程是什么?
守護線程(即daemon thread),是個服務線程,准確地來說就是服務其他的線程。
72. 創建線程有哪幾種方式?
①. 繼承Thread類創建線程類

定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱為執行體。
創建Thread子類的實例,即創建了線程對象。
調用線程對象的start()方法來啟動該線程。
②. 通過Runnable接口創建線程類

定義runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體。
創建 Runnable實現類的實例,並依此實例作為Thread的target來創建Thread對象,該Thread對象才是真正的線程對象。
調用線程對象的start()方法來啟動該線程。
③. 通過Callable和Future創建線程

創建Callable接口的實現類,並實現call()方法,該call()方法將作為線程執行體,並且有返回值。
創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。
使用FutureTask對象作為Thread對象的target創建並啟動新線程。
調用FutureTask對象的get()方法來獲得子線程執行結束后的返回值。
73. 說一下 runnable 和 callable 有什么區別?
Runnable接口中的run()方法的返回值是void,它做的事情只是純粹地去執行run()方法中的代碼而已;
Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合可以用來獲取異步執行的結果。
74.線程有哪些狀態?
線程通常都有五種狀態,創建、就緒、運行、阻塞和死亡。

創建狀態。在生成線程對象,並沒有調用該對象的start方法,這是線程處於創建狀態。
就緒狀態。當調用了線程對象的start方法之后,該線程就進入了就緒狀態,但是此時線程調度程序還沒有把該線程設置為當前線程,此時處於就緒狀態。在線程運行之后,從等待或者睡眠中回來之后,也會處於就緒狀態。
運行狀態。線程調度程序將處於就緒狀態的線程設置為當前線程,此時線程就進入了運行狀態,開始運行run函數當中的代碼。
阻塞狀態。線程正在運行的時候,被暫停,通常是為了等待某個時間的發生(比如說某項資源就緒)之后再繼續運行。sleep,suspend,wait等方法都可以導致線程阻塞。
死亡狀態。如果一個線程的run方法執行結束或者調用stop方法后,該線程就會死亡。對於已經死亡的線程,無法再使用start方法令其進入就緒   
75.sleep() 和 wait() 有什么區別?
sleep():方法是線程類(Thread)的靜態方法,讓調用線程進入睡眠狀態,讓出執行機會給其他線程,等到休眠時間結束后,線程進入就緒狀態和其他線程一起競爭cpu的執行時間。因為sleep() 是static靜態的方法,他不能改變對象的機鎖,當一個synchronized塊中調用了sleep() 方法,線程雖然進入休眠,但是對象的機鎖沒有被釋放,其他線程依然無法訪問這個對象。

wait():wait()是Object的方法,當一個線程執行到wait方法時,它就進入到一個和該對象相關的等待池,同時釋放對象的機鎖,使得其他線程能夠訪問,可以通過notify,notifyAll方法來喚醒等待的線程。
76.notify()和 notifyAll()有什么區別?
如果線程調用了對象的 wait()方法,那么線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
當有線程調用了對象的 notifyAll()方法(喚醒所有 wait 線程)或 notify()方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了notify后只要一個線程會由等待池進入鎖池,而notifyAll會將該對象等待池內的所有線程移動到鎖池中,等待鎖競爭。
優先級高的線程競爭到對象鎖的概率大,假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調用 wait()方法,它才會重新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。
77.線程的 run()和 start()有什么區別?
每個線程都是通過某個特定Thread對象所對應的方法run()來完成其操作的,方法run()稱為線程體。通過調用Thread類的start()方法來啟動一個線程。

start()方法來啟動一個線程,真正實現了多線程運行。這時無需等待run方法體代碼執行完畢,可以直接繼續執行下面的代碼; 這時此線程是處於就緒狀態, 並沒有運行。 然后通過此Thread類調用方法run()來完成其運行狀態, 這里方法run()稱為線程體,它包含了要執行的這個線程的內容, Run方法運行結束, 此線程終止。然后CPU再調度其它線程。

run()方法是在本線程里的,只是線程里的一個函數,而不是多線程的。 如果直接調用run(),其實就相當於是調用了一個普通函數而已,直接待用run()方法必須等待run()方法執行完畢才能執行下面的代碼,所以執行路徑還是只有一條,根本就沒有線程的特征,所以在多線程執行時要使用start()方法而不是run()方法。
78.創建線程池有哪幾種方式?
①. newFixedThreadPool(int nThreads)

創建一個固定長度的線程池,每當提交一個任務就創建一個線程,直到達到線程池的最大數量,這時線程規模將不再變化,當線程發生未預期的錯誤而結束時,線程池會補充一個新的線程。

②. newCachedThreadPool()

創建一個可緩存的線程池,如果線程池的規模超過了處理需求,將自動回收空閑線程,而當需求增加時,則可以自動添加新線程,線程池的規模不存在任何限制。

③. newSingleThreadExecutor()

這是一個單線程的Executor,它創建單個工作線程來執行任務,如果這個線程異常結束,會創建一個新的來替代它;它的特點是能確保依照任務在隊列中的順序來串行執行。

④. newScheduledThreadPool(int corePoolSize)

創建了一個固定長度的線程池,而且以延遲或定時的方式來執行任務,類似於Timer。
參考鏈接
79.線程池都有哪些狀態?
線程池有5種狀態:Running、ShutDown、Stop、Tidying、Terminated。

線程池各個狀態切換框架圖:

80.線程池中 submit()和 execute()方法有什么區別?
接收的參數不一樣
submit有返回值,而execute沒有
submit方便Exception處理
81. 在 java 程序中怎么保證多線程的運行安全?

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

原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操作,(atomic,synchronized);
可見性:一個線程對主內存的修改可以及時地被其他線程看到,(synchronized,volatile);
有序性:一個線程觀察其他線程中的指令執行順序,由於指令重排序,該觀察結果一般雜亂無序,(happens-before原則)。
82.多線程鎖的升級原理是什么?

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

鎖升級的圖示過程:

83.什么是死鎖?
死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。是操作系統層面的一個錯誤,是進程死鎖的簡稱,最早在 1965 年由 Dijkstra 在研究銀行家算法時提出的,它是計算機操作系統乃至整個並發程序設計領域最難處理的問題之一。
84.ThreadLocal 是什么?有哪些使用場景?
線程局部變量是局限於線程內部的變量,屬於線程自身所有,不在多個線程間共享。Java提供ThreadLocal類來支持線程局部變量,是一種實現線程安全的方式。但是在管理環境下(如 web 服務器)使用線程局部變量的時候要特別小心,在這種情況下,工作線程的生命周期比任何應用變量的生命周期都要長。任何線程局部變量一旦在工作完成后沒有釋放,Java 應用就存在內存泄露的風險。
85.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鎖適合代碼少量的同步問題。
86.對ajax的理解
Ajax為異步請求,即局部刷新技術,在傳統的頁面中,用戶需要點擊按鈕或者事件觸發請求,到刷新頁面,而異步技術為不需要點擊即可觸發事件,這樣使得用戶體驗感增強,比如商城購物車的異步加載,當你點擊商品時無需請求后台而直接動態修改參數。

6.數據庫知識
1.數據庫隔離級別有哪些,各自的含義是什么,MYSQL 默認的隔離級別是是什么。

1.未提交讀(Read Uncommitted):允許臟讀,也就是可能讀取到其他會話中未提交事務修改的數據
2.提交讀(Read Committed):只能讀取到已經提交的數據。Oracle等多數數據庫默認都是該級別 (不重復讀)
3.可重復讀(Repeated Read):可重復讀。在同一個事務內的查詢都是事務開始時刻一致的,InnoDB默認級別。在SQL標准中,該隔離級別消除了不可重復讀,但是還存在幻象讀
4.串行讀(Serializable):完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞

MYSQL默認是RepeatedRead級別
1
2
3
4
5
6
2.MYSQL 有哪些存儲引擎,各自優缺點。

1.MyISAM: 擁有較高的插入,查詢速度,但不支持事務
2.InnoDB :5.5版本后Mysql的默認數據庫,事務型數據庫的首選引擎,支持ACID事務,支持行級鎖定
3.BDB: 源自Berkeley DB,事務型數據庫的另一種選擇,支持COMMIT和ROLLBACK等其他事務特性
4.Memory :所有數據置於內存的存儲引擎,擁有極高的插入,更新和查詢效率。但是會占用和數據量成正比的內存空間。並且其內容會在Mysql重新啟動時丟失
5.Merge :將一定數量的MyISAM表聯合而成一個整體,在超大規模數據存儲時很有用
6.Archive :非常適合存儲大量的獨立的,作為歷史記錄的數據。因為它們不經常被讀取。Archive擁有高效的插入速度,但其對查詢的支持相對較差
7.Federated: 將不同的Mysql服務器聯合起來,邏輯上組成一個完整的數據庫。非常適合分布式應用
8.Cluster/NDB :高冗余的存儲引擎,用多台數據機器聯合提供服務以提高整體性能和安全性。適合數據量大,安全和性能要求高的應用
9.CSV: 邏輯上由逗號分割數據的存儲引擎。它會在數據庫子目錄里為每個數據表創建一個.CSV文件。這是一種普通文本文件,每個數據行占用一個文本行。CSV存儲引擎不支持索引。
10.BlackHole :黑洞引擎,寫入的任何數據都會消失,一般用於記錄binlog做復制的中繼

另外,Mysql的存儲引擎接口定義良好。有興趣的開發者通過閱讀文檔編寫自己的存儲引擎。
1
2
3
4
5
6
7
8
9
10
11
12
參考鏈接
3.高並發下,如何做到安全的修改同一行數據。
使用悲觀鎖 悲觀鎖本質是當前只有一個線程執行操作,結束了喚醒其他線程進行處理。
也可以緩存隊列中鎖定主鍵。
4.樂觀鎖和悲觀鎖是什么,INNODB 的行級鎖有哪 2 種,解釋其含義。
樂觀鎖是設定每次修改都不會沖突,只在提交的時候去檢查,悲觀鎖設定每次修改都會沖突,持有排他鎖。
行級鎖分為共享鎖和排他鎖兩種 共享鎖又稱讀鎖 排他鎖又稱寫鎖
參考鏈接
5.SQL 優化的一般步驟是什么,怎么看執行計划,如何理解其中各個字段的含義。
查看慢日志(show [session|gobal] status ),定位慢查詢,查看慢查詢執行計划 根據執行計划確認優化方案
Explain sql
select_type:表示select類型。常見的取值有SIMPLE(簡單表,即不使用連接或者子查詢)、PRIMARY(主查詢,即外層的查詢)、UNION(union中的第二個或者后面的查詢語句)、SUBQUERY(子查詢中的第一個SELECT)等。
talbe:輸出結果集的表。
type:表的連接類型。性能由高到底:system(表中僅有一行)、const(表中最多有一個匹配行)、eq_ref、ref、ref_null、index_merge、unique_subquery、index_subquery、range、idnex等
possible_keys:查詢時,可能使用的索引
key:實際使用的索引
key_len:索引字段的長度
rows:掃描行的數量
Extra:執行情況的說明和描述
Oracle優化

1.對查詢進行優化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。

2.應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num is null
可以在num上設置默認值0,確保表中num列沒有null值,然后這樣查詢:
select id from t where num=0
3.應盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。
4.應盡量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or num=20
可以這樣查詢:
select id from t where num=10
union all
select id from t where num=20
5.in 和 not in 也要慎用,否則會導致全表掃描,如:
select id from t where num in(1,2,3)
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.下面的查詢也將導致全表掃描:
select id from t where name like ‘%abc%’
若要提高效率,可以考慮全文檢索。
7.如果在 where 子句中使用參數,也會導致全表掃描。因為SQL只有在運行時才會解析局部變量,但優化程序不能將訪問計划的選擇推遲到運行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計划,變量的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:
select id from t where num=@num
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where num=@num
8.應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num/2=100
應改為:
select id from t where num=100*2
9.應盡量避免在where子句中對字段進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3)=‘abc’ // oracle總有的是substr函數。
select id from t where datediff(day,createdate,‘2005-11-30’)=0 //查過了確實沒有datediff函數。
應改為:
select id from t where name like ‘abc%’
select id from t where createdate>=‘2005-11-30’ and createdate<‘2005-12-1’ //
oracle 中時間應該把char 轉換成 date 如: createdate >= to_date(‘2005-11-30’,‘yyyy-mm-dd’)
10.不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。

11.在使用索引字段作為條件時,如果該索引是復合索引,那么必須使用到該索引中的第一個字段作為條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應盡可能的讓字段順序與索引順序相一致。
12.不要寫一些沒有意義的查詢,如需要生成一個空表結構:
select col1,col2 into #t from t where 1=0
這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:
create table #t(…)
13.很多時候用 exists 代替 in 是一個好的選擇:
select num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
14.並不是所有索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重復時,SQL查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那么即使在sex上建了索引也對查詢效率起不了作用。
15.索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。
16.應盡可能的避免更新 clustered 索引數據列,因為 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那么需要考慮是否應將該索引建為 clustered 索引。
17.盡量使用數字型字段,若只含數值信息的字段盡量不要設計為字符型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因為引擎在處理查詢和連接時會逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了。
18.盡可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。
19.任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。
20.盡量使用表變量來代替臨時表。如果表變量包含大量數據,請注意索引非常有限(只有主鍵索引)。
21.避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。
22.臨時表並不是不可使用,適當地使用它們可以使某些例程更有效,例如,當需要重復引用大型表或常用表中的某個數據集時。但是,對於一次性事件,最好使用導出表。
23.在新建臨時表時,如果一次性插入數據量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,為了緩和系統表的資源,應先create table,然后insert。
24.如果使用到了臨時表,在存儲過程的最后務必將所有的臨時表顯式刪除,先 truncate table ,然后 drop table ,這樣可以避免系統表的較長時間鎖定。
25.盡量避免使用游標,因為游標的效率較差,如果游標操作的數據超過1萬行,那么就應該考慮改寫。
26.使用基於游標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。
27.與臨時表一樣,游標並不是不可使用。對小型數據集使用 FAST_FORWARD 游標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數據時。在結果集中包括“合計”的例程通常要比使用游標執行的速度快。如果開發時間允許,基於游標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。
28.在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每個語句后向客戶端發送 DONE_IN_PROC 消息。
29.盡量避免大事務操作,提高系統並發能力。
30.盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
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
參考鏈接1
參考鏈接2
6.數據庫會死鎖嗎,舉一個死鎖的例子,mysql 怎么解決死鎖。
產生死鎖的原因主要是:
(1)系統資源不足。
(2) 進程運行推進的順序不合適。
(3)資源分配不當等。
如果系統資源充足,進程的資源請求都能夠得到滿足,死鎖出現的可能性就很低,否則就會因爭奪有限的資源而陷入死鎖。其次,進程運行推進順序與速度不同,也可能產生死鎖。
產生死鎖的四個必要條件:
(1) 互斥條件:一個資源每次只能被一個進程使用。
(2) 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
(3) 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
(4) 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖。
這里提供兩個解決數據庫死鎖的方法:
1)重啟數據庫(誰用誰知道)
2)殺掉搶資源的進程:
先查哪些進程在搶資源:SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
殺掉它們:Kill trx_mysql_thread_id;
7.MYsql 的索引原理,索引的類型有哪些,如何創建合理的索引,索引如何優化。
索引是通過復雜的算法,提高數據查詢性能的手段。從磁盤io到內存io的轉變
普通索引,主鍵,唯一,單列/多列索引建索引的幾大原則
1.最左前綴匹配原則,非常重要的原則,mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整。
2.=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式
3.盡量選擇區分度高的列作為索引,區分度的公式是count(distinct col)/count(*),表示字段不重復的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,那可能有人會問,這個比例有什么經驗值嗎?使用場景不同,這個值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條記錄
4.索引列不能參與計算,保持列“干凈”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);
5.盡量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那么只需要修改原來的索引即可
參考鏈接1
參考鏈接2
##聚集索引和非聚集索引的區別。
“聚簇”就是索引和記錄緊密在一起。
非聚簇索引 索引文件和數據文件分開存放,索引文件的葉子頁只保存了主鍵值,要定位記錄還要去查找相應的數據塊。
8.數據庫中 BTREE 和 B+tree 區別。
B+是btree的變種,本質都是btree,btree+與B-Tree相比,B+Tree有以下不同點:
每個節點的指針上限為2d而不是2d+1。
內節點不存儲data,只存儲key;葉子節點不存儲指針。
參考鏈接
Btree 怎么分裂的,什么時候分裂,為什么是平衡的。
Key 超過1024才分裂B樹為甚會分裂? 因為隨着數據的增多,一個結點的key滿了,為了保持B樹的特性,就會產生分裂,就向紅黑樹和AVL樹為了保持樹的性質需要進行旋轉一樣!
9.ACID 是什么。
A,atomic,原子性,要么都提交,要么都失敗,不能一部分成功,一部分失敗。
C,consistent,一致性,事物開始及結束后,數據的一致性約束沒有被破壞
I,isolation,隔離性,並發事物間相互不影響,互不干擾。
D,durability,持久性,已經提交的事物對數據庫所做的更新必須永久保存。即便發生崩潰,也不能被回滾或數據丟失。
##Mysql 怎么優化 table scan 的。
避免在where子句中對字段進行is null判斷
應盡量避免在where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。
避免在where 子句中使用or 來連接條件
in 和not in 也要慎用
Like查詢(非左開頭)
使用NUM=@num參數這種
where 子句中對字段進行表達式操作num/2=XX
在where子句中對字段進行函數操作
10.如何寫 sql 能夠有效的使用到復合索引。
由於復合索引的組合索引,類似多個木板拼接在一起,如果中間斷了就無法用了,所以要能用到復合索引,首先開頭(第一列)要用上,比如index(a,b) 這種,我們可以select table tname where a=XX 用到第一列索引 如果想用第二列 可以 and b=XX 或者and b like‘TTT%’
11.mysql 中 in 和 exists 區別。
mysql中的in語句是把外表和內表作hash 連接,而exists語句是對外表作loop循環,每次loop循環再對內表進行查詢。一直大家都認為exists比in語句的效率要高,這種說法其實是不准確的。這個是要區分環境的。
如果查詢的兩個表大小相當,那么用in和exists差別不大。
如果兩個表中一個較小,一個是大表,則子查詢表大的用exists,子查詢表小的用in:
not in 和not exists如果查詢語句使用了not in 那么內外表都進行全表掃描,沒有用到索引;而not extsts 的子查詢依然能用到表上的索引。所以無論那個表大,用not exists都比not in要快。
1.EXISTS只返回TRUE或FALSE,不會返回UNKNOWN。
2.IN當遇到包含NULL的情況,那么就會返回UNKNOWN。
12.數據庫自增主鍵可能的問題。
在分庫分表時可能會生成重復主鍵 利用自增比例達到唯一 自增1 2,3 等
參考鏈接
#消息隊列
##用過哪些 MQ,和其他 mq 比較有什么優缺點,MQ 的連接是線程安全的嗎,你們公司的MQ 服務架構怎樣的。
根據實際情況說明
我們公司用activeMQ 因為業務比較簡單 只有轉碼功能,而amq比較簡單
如果是分布式的建議用kafka
參考鏈接
13.MQ 系統的數據如何保證不丟失。
基本都是對數據進行持久化,多盤存儲
14.rabbitmq 如何實現集群高可用。
集群是保證服務可靠性的一種方式,同時可以通過水平擴展以提升消息吞吐能力。RabbitMQ是用分布式程序設計語言erlang開發的,所以天生就支持集群。接下來,將介紹RabbitMQ分布式消息處理方式、集群模式、節點類型,並動手搭建一個高可用集群環境,最后通過java程序來驗證集群的高可用性。
三種分布式消息處理方式
RabbitMQ分布式的消息處理方式有以下三種:
1、Clustering:不支持跨網段,各節點需運行同版本的Erlang和RabbitMQ, 應用於同網段局域網。
2、Federation:允許單台服務器上的Exchange或Queue接收發布到另一台服務器上Exchange或Queue的消息, 應用於廣域網,。
3、Shovel:與Federation類似,但工作在更低層次。
RabbitMQ對網絡延遲很敏感,在LAN環境建議使用clustering方式;在WAN環境中,則使用Federation或Shovel。我們平時說的RabbitMQ集群,說的就是clustering方式,它是RabbitMQ內嵌的一種消息處理方式,而Federation或Shovel則是以plugin形式存在。
參考鏈接1
參考鏈接2
#Redis,Memcached
15.redis 的 list 結構相關的操作。
LPUSH LPUSHX RPUSH RPUSHX LPOP RPOP BLPOP BRPOP LLEN LRANGE
參考鏈接
16.Redis 的數據結構都有哪些。
字符串(strings):存儲整數(比如計數器)和字符串(廢話。。),有些公司也用來存儲json/pb等序列化數據,並不推薦,浪費內存
哈希表(hashes):存儲配置,對象(比如用戶、商品),優點是可以存取部分key,對於經常變化的或者部分key要求atom操作的適合
列表(lists):可以用來存最新用戶動態,時間軸,優點是有序,確定是元素可重復,不去重
集合(sets):無序,唯一,對於要求嚴格唯一性的可以使用
有序集合(sorted sets):集合的有序版,很好用,對於排名之類的復雜場景可以考慮
參考鏈接
##Redis 的使用要注意什么,講講持久化方式,內存設置,集群的應用和優劣勢,淘汰策略等。
持久化方式:RDB時間點快照 AOF記錄服務器執行的所有寫操作命令,並在服務器啟動時,通過重新執行這些命令來還原數據集。
內存設置 maxmemory used_memory
虛擬內存: vm-enabled yes
3.0采用Cluster方式,
Redis集群相對單機在功能上存在一些限制, 需要開發人員提前了解,
在使用時做好規避。 限制如下:
1) key批量操作支持有限。 如mset、 mget, 目前只支持具有相同slot值的
ke
y執
行批量操作。 對於映射為不同slot值的key由於執行mget、 mget等操作可
能存在於多個節點上因此不被支持。
2) key事務操作支持有限。 同理只支持多key在同一節點上的事務操
作, 當多個key分布在不同的節點上時無法使用事務功能。
3) key作為數據分區的最小粒度, 因此不能將一個大的鍵值對象如
ha
sh、 list等映射到不同的節點。
4) 不支持多數據庫空間。 單機下的Redis可以支持16個數據庫, 集群模
式下只能使用一個數據庫空間, 即db0。
5) 復制結構只支持一層, 從節點只能復制主節點, 不支持嵌套樹狀復
制結構。
Redis Cluster是Redis的分布式解決方案, 在3.0版本正式推出, 有效地解
決了Redis分布式方面的需求。 當遇到單機內存、 並發、 流量等瓶頸時, 可
以采用Cluster架構方案達到負載均衡的目的。 之前, Redis分布式方案一般
有兩種:
·客戶端分區方案, 優點是分區邏輯可控, 缺點是需要自己處理數據路
由、 高可用、 故障轉移等問題。
·代理方案, 優點是簡化客戶端分布式邏輯和升級維護便利, 缺點是加
重架構部署復雜度和性能損耗。
現在官方為我們提供了專有的集群方案: Redis Cluster, 它非常優雅地
解決了Redis集群方面的問題, 因此理解應用好Redis Cluster將極大地解放我
們使用分布式Redis的工作量, 同時它也是學習分布式存儲的絕佳案例。
LRU(近期最少使用算法)TTL(超時算法) 去除ttl最大的鍵值
參考鏈接1
參考鏈接2
參考鏈接3
17.redis2 和 redis3 的區別,redis3 內部通訊機制。
集群方式的區別,3采用Cluster,2采用客戶端分區方案和代理方案
通信過程說明:
1) 集群中的每個節點都會單獨開辟一個TCP通道, 用於節點之間彼此
通信, 通信端口號在基礎端口上加10000。
2) 每個節點在固定周期內通過特定規則選擇幾個節點發送ping消息。
3) 接收到ping消息的節點用pong消息作為響應。
##當前 redis 集群有哪些玩法,各自優缺點,場景。
當緩存使用 持久化使用
18.Memcache 的原理,哪些數據適合放在緩存中。
基於libevent的事件處理
內置內存存儲方式SLab Allocation機制
並不單一的數據刪除機制
基於客戶端的分布式系統
變化頻繁,具有不穩定性的數據,不需要實時入庫, (比如用戶在線
狀態、在線人數…)
門戶網站的新聞等,覺得頁面靜態化仍不能滿足要求,可以放入
到memcache中.(配合jquey的ajax請求)
##redis 和 memcached 的內存管理的區別。
Memcached默認使用Slab Allocation機制管理內存,其主要思想是按照預先規定的大小,將分配的內存分割成特定長度的塊以存儲相應長度的key-value數據記錄,以完全解決內存碎片問題。
Redis的內存管理主要通過源碼中zmalloc.h和zmalloc.c兩個文件來實現的。
在Redis中,並不是所有的數據都一直存儲在內存中的。這是和Memcached相比一個最大的區別。
參考鏈接
19.Redis 的並發競爭問題如何解決,了解 Redis 事務的 CAS 操作嗎。
Redis為單進程單線程模式,采用隊列模式將並發訪問變為串行訪問。Redis本身沒有鎖的概念,Redis對於多個客戶端連接並不存在競爭,但是在Jedis客戶端對Redis進行並發訪問時會發生連接超時、數據轉換錯誤、阻塞、客戶端關閉連接等問題,這些問題均是由於客戶端連接混亂造成。對此有2種解決方法:
1.客戶端角度,為保證每個客戶端間正常有序與Redis進行通信,對連接進行池化,同時對客戶端讀寫Redis操作采用內部鎖synchronized。
2.服務器角度,利用setnx實現鎖。
MULTI,EXEC,DISCARD,WATCH 四個命令是 Redis 事務的四個基礎命令。其中:
MULTI,告訴 Redis 服務器開啟一個事務。注意,只是開啟,而不是執行
EXEC,告訴 Redis 開始執行事務
DISCARD,告訴 Redis 取消事務
WATCH,監視某一個鍵值對,它的作用是在事務執行之前如果監視的鍵值被修改,事務會被取消。
可以利用watch實現cas樂觀鎖
參考鏈接1
參考鏈接2
##Redis 的選舉算法和流程是怎樣的
Raft采用心跳機制觸發Leader選舉。系統啟動后,全部節點初始化為Follower,term為0.節點如果收到了RequestVote或者AppendEntries,就會保持自己的Follower身份。如果一段時間內沒收到AppendEntries消息直到選舉超時,說明在該節點的超時時間內還沒發現Leader,Follower就會轉換成Candidate,自己開始競選Leader。一旦轉化為Candidate,該節點立即開始下面幾件事情:
1、增加自己的term。
2、啟動一個新的定時器。
3、給自己投一票。
4、向所有其他節點發送RequestVote,並等待其他節點的回復。
如果在這過程中收到了其他節點發送的AppendEntries,就說明已經有Leader產生,自己就轉換成Follower,選舉結束。
如果在計時器超時前,節點收到多數節點的同意投票,就轉換成Leader。同時向所有其他節點發送AppendEntries,告知自己成為了Leader。
每個節點在一個term內只能投一票,采取先到先得的策略,Candidate前面說到已經投給了自己,Follower會投給第一個收到RequestVote的節點。每個Follower有一個計時器,在計時器超時時仍然沒有接受到來自Leader的心跳RPC, 則自己轉換為Candidate, 開始請求投票,就是上面的的競選Leader步驟。
如果多個Candidate發起投票,每個Candidate都沒拿到多數的投票(Split Vote),那么就會等到計時器超時后重新成為Candidate,重復前面競選Leader步驟。
Raft協議的定時器采取隨機超時時間,這是選舉Leader的關鍵。每個節點定時器的超時時間隨機設置,隨機選取配置時間的1倍到2倍之間。由於隨機配置,所以各個Follower同時轉成Candidate的時間一般不一樣,在同一個term內,先轉為Candidate的節點會先發起投票,從而獲得多數票。多個節點同時轉換為Candidate的可能性很小。即使幾個Candidate同時發起投票,在該term內有幾個節點獲得一樣高的票數,只是這個term無法選出Leader。由於各個節點定時器的超時時間隨機生成,那么最先進入下一個term的節點,將更有機會成為Leader。連續多次發生在一個term內節點獲得一樣高票數在理論上幾率很小,實際上可以認為完全不可能發生。一般1-2個term類,Leader就會被選出來。
Sentinel的選舉流程
Sentinel集群正常運行的時候每個節點epoch相同,當需要故障轉移的時候會在集群中選出Leader執行故障轉移操作。Sentinel采用了Raft協議實現了Sentinel間選舉Leader的算法,不過也不完全跟論文描述的步驟一致。Sentinel集群運行過程中故障轉移完成,所有Sentinel又會恢復平等。Leader僅僅是故障轉移操作出現的角色。
選舉流程
1、某個Sentinel認定master客觀下線的節點后,該Sentinel會先看看自己有沒有投過票,如果自己已經投過票給其他Sentinel了,在2倍故障轉移的超時時間自己就不會成為Leader。相當於它是一個Follower。
2、如果該Sentinel還沒投過票,那么它就成為Candidate。
3、和Raft協議描述的一樣,成為Candidate,Sentinel需要完成幾件事情
1)更新故障轉移狀態為start
2)當前epoch加1,相當於進入一個新term,在Sentinel中epoch就是Raft協議中的term。
3)更新自己的超時時間為當前時間隨機加上一段時間,隨機時間為1s內的隨機毫秒數。
4)向其他節點發送is-master-down-by-addr命令請求投票。命令會帶上自己的epoch。
5)給自己投一票,在Sentinel中,投票的方式是把自己master結構體里的leader和leader_epoch改成投給的Sentinel和它的epoch。
4、其他Sentinel會收到Candidate的is-master-down-by-addr命令。如果Sentinel當前epoch和Candidate傳給他的epoch一樣,說明他已經把自己master結構體里的leader和leader_epoch改成其他Candidate,相當於把票投給了其他Candidate。投過票給別的Sentinel后,在當前epoch內自己就只能成為Follower。
5、Candidate會不斷的統計自己的票數,直到他發現認同他成為Leader的票數超過一半而且超過它配置的quorum(quorum可以參考《redis sentinel設計與實現》)。Sentinel比Raft協議增加了quorum,這樣一個Sentinel能否當選Leader還取決於它配置的quorum。
6、如果在一個選舉時間內,Candidate沒有獲得超過一半且超過它配置的quorum的票數,自己的這次選舉就失敗了。
7、如果在一個epoch內,沒有一個Candidate獲得更多的票數。那么等待超過2倍故障轉移的超時時間后,Candidate增加epoch重新投票。
8、如果某個Candidate獲得超過一半且超過它配置的quorum的票數,那么它就成為了Leader。
9、與Raft協議不同,Leader並不會把自己成為Leader的消息發給其他Sentinel。其他Sentinel等待Leader從slave選出master后,檢測到新的master正常工作后,就會去掉客觀下線的標識,從而不需要進入故障轉移流程。
參考鏈接
20.redis 的持久化的機制,aof 和 rdb 的區別。
RDB 定時快照方式(snapshot): 定時備份,可能會丟失數據
AOF 基於語句追加方式 只追加寫操作
AOF 持久化和 RDB 持久化的最主要區別在於,前者記錄了數據的變更,而后者是保存了數據本身
21.redis 的集群怎么同步的數據的。
redis replication redis-migrate-tool等方式
#搜索
22.elasticsearch 了解多少,說說你們公司 es 的集群架構,索引數據大小,分片有多少,以及一些調優手段。elasticsearch 的倒排索引是什么。
ElasticSearch(簡稱ES)是一個分布式、Restful的搜索及分析服務器,設計用於分布式計算;能夠達到實時搜索,穩定,可靠,快速。和Apache Solr一樣,它也是基於Lucence的索引服務器,而ElasticSearch對比Solr的優點在於:

1.輕量級:安裝啟動方便,下載文件之后一條命令就可以啟動。
2.Schema free:可以向服務器提交任意結構的JSON對象,Solr中使用schema.xml指定了索引結構。
3.多索引文件支持:使用不同的index參數就能創建另一個索引文件,Solr中需要另行配置。
4.分布式:Solr Cloud的配置比較復雜。
1
2
3
4
倒排索引是實現“單詞-文檔矩陣”的一種具體存儲形式,通過倒排索引,可以根據單詞快速獲取包含這個單詞的文檔列表。倒排索引主要由兩個部分組成:“單詞詞典”和“倒排文件”。
##elasticsearch 索引數據多了怎么辦,如何調優,部署。
使用bulk API
初次索引的時候,把 replica 設置為 0
增大 threadpool.index.queue_size
增大 indices.memory.index_buffer_size
增大 index.translog.flush_threshold_ops
增大 index.translog.sync_interval
增大 index.engine.robin.refresh_interval
參考鏈接
23.lucence 內部結構是什么
索引(Index):
在Lucene中一個索引是放在一個文件夾中的。
如上圖,同一文件夾中的所有的文件構成一個Lucene索引。
段(Segment):
一個索引可以包含多個段,段與段之間是獨立的,添加新文檔可以生成新的段,不同的段可以合並。
如上圖,具有相同前綴文件的屬同一個段,圖中共三個段 “_0” 和 "_1"和“_2”。
segments.gen和segments_X是段的元數據文件,也即它們保存了段的屬性信息。
文檔(Document):
文檔是我們建索引的基本單位,不同的文檔是保存在不同的段中的,一個段可以包含多篇文檔。
新添加的文檔是單獨保存在一個新生成的段中,隨着段的合並,不同的文檔合並到同一個段中。
域(Field):
一篇文檔包含不同類型的信息,可以分開索引,比如標題,時間,正文,作者等,都可以保存在不同的域里。
不同域的索引方式可以不同,在真正解析域的存儲的時候,我們會詳細解讀。
詞(Term):
詞是索引的最小單位,是經過詞法分析和語言處理后的字符串
————————————————
版權聲明:本文為CSDN博主「是華仔呀」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_43495390/java/article/details/86533482


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM