## 1. 單例模式介紹
### 1.1 定義
保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
### 1.2 為什么要用單例模式呢?
在我們的系統中,有一些對象其實我們只需要一個,比如說: ***線程池、緩存、對話框、注冊表、日志對象、充當打印機、顯卡*** 等設備驅動程序的對象。事實上,這一類對象只能有一個實例,如果制造出多個實例就可能會導致一些問題的產生,比如:程序的行為異常、資源使用過量、或者不一致性的結果。
**簡單來說使用單例模式可以帶來下面幾個好處:**
1. 對于頻繁使用的對象,可以省略創建對象所花費的時間,這對于那些重量級對象而言,是非常可觀的一筆系統開銷;
2. 由于 new 操作的次數減少,因而對系統內存的使用頻率也會降低,這將減輕 GC 壓力,縮短 GC 停頓時間。
### 1.3 為什么不使用全局變量確保一個類只有一個實例呢?
我們知道全局變量分為 ***靜態變量*** 和 ***實例變量***,靜態變量也可以保證該類的實例只存在一個。
只要程序加載了類的字節碼,不用創建任何實例對象,靜態變量就會被分配空間,靜態變量就可以被使用了。
但是,如果說這個對象非常消耗資源,而且程序某次的執行中一直沒用,這樣就造成了資源的浪費。利用單例模式的話,我們就可以實現 ***在需要使用時才創建對象***,這樣就避免了不必要的資源浪費。?不僅僅是因為這個原因,在程序中我們要盡量避免全局變量的使用,大量使用全局變量給程序的調試、維護等帶來困難。
*****
## 2 單例的模式的實現
通常單例模式在Java語言中,有兩種構建方式:
* 餓漢方式。指全局的單例實例在類裝載時構建
* 懶漢方式。指全局的單例實例在第一次被使用時構建。
不管是那種創建方式,它們通常都存在下面幾點相似處:
* 單例類必須要有一個 private 訪問級別的構造函數,只有這樣,才能確保單例不會在系統中的其他代碼內被實例化;
* instance 成員變量和 uniqueInstance 方法必須是 static 的。
### 2.1 餓漢方式(線程安全)
```
public class Singleton {
//在靜態初始化器中創建單例實例,這段代碼保證了線程安全
private static Singleton uniqueInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return uniqueInstance;
}
}
```
所謂?“餓漢方式”?就是說JVM在加載這個類時就馬上創建此唯一的單例實例,不管你用不用,先創建了再說,如果一直沒有被使用,便浪費了空間,典型的空間換時間,每次調用的時候,就不需要再判斷,節省了運行時間。
### 2.2 懶漢式(非線程安全和synchronized關鍵字線程安全版本 )
```
public class Singleton {
private static Singleton uniqueInstance;
private Singleton (){
}
//沒有加入synchronized關鍵字的版本是線程不安全的
public static Singleton getInstance() {
//判斷當前單例是否已經存在,若存在則返回,不存在則再建立單例
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
```
所謂?“餓漢方式”?就是說單例實例在第一次被使用時構建,而不是在JVM在加載這個類時就馬上創建此唯一的單例實例。
但是上面這種方式很明顯是線程不安全的,如果多個線程同時訪問getInstance()方法時就會出現問題。如果想要保證線程安全,一種比較常見的方式就是在getInstance() 方法前加上synchronized關鍵字,如下:
```
public static synchronized Singleton getInstance() {
if (instance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
```
我們知道synchronized關鍵字偏重量級鎖。雖然在JavaSE1.6之后synchronized關鍵字進行了主要包括:為了減少獲得鎖和釋放鎖帶來的性能消耗而引入的偏向鎖和輕量級鎖以及其它各種優化之后執行效率有了顯著提升。
但是在程序中每次使用getInstance() 都要經過synchronized加鎖這一層,這難免會增加getInstance()的方法的時間消費,而且還可能會發生阻塞。我們下面介紹到的?雙重檢查加鎖版本?就是為了解決這個問題而存在的。
### 2.3 懶漢式(雙重檢查加鎖版本)
利用雙重檢查加鎖(double-checked locking),首先檢查是否實例已經創建,如果尚未創建,“才”進行同步。這樣以來,只有一次同步,這正是我們想要的效果。
```
public class Singleton {
//volatile保證,當uniqueInstance變量被初始化成Singleton實例時,
//多個線程可以正確處理uniqueInstance變量
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getInstance() {
//檢查實例,如果不存在,就進入同步代碼塊
if (uniqueInstance == null) {
//只有第一次才徹底執行這里的代碼
synchronized(Singleton.class) {
//進入同步代碼塊后,再檢查一次,如果仍是null,才創建實例
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
```
很明顯,這種方式相比于使用synchronized關鍵字的方法,可以大大減少getInstance() 的時間消費。
我們上面使用到了volatile關鍵字來保證數據的可見性,uniqueInstance 采用 volatile 關鍵字修飾也是很有必要。 uniqueInstance 采用 volatile 關鍵字修飾也是很有必要的, uniqueInstance = new Singleton();
這段代碼其實是分為三步執行:
1. 為 uniqueInstance 分配內存空間
2. 初始化 uniqueInstance
3. 將 uniqueInstance 指向分配的內存地址 但是由于 JVM 具有指令重排的特性,執行順序有可能變成 1->3->2。
指令重排 在單線程環境下不會出現問題,但是在多線程環境下會導致一個線程獲得還沒有 初始化的實例。例如,線程 T1 執行了 1 和 3,此時 T2 調用 getUniqueInstance() 后發現 uniqueInstance 不為空,因此返回 uniqueInstance,但此時 uniqueInstance 還未被初始化。 使用 volatile 可以禁止 JVM 的指令重排,保證在多線程環境下也能正常運行。
### 2.4 懶漢式(登記式/靜態內部類方式)
靜態內部實現的單例是懶加載的且線程安全。
只有通過顯式調用 getInstance 方法時,才會顯式裝載 SingletonHolder 類,從而實例化 instance(只有第一次使用這個單例的實例的時候才加載,同時不會有線程安全問題)。
```
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
```
### 2.5 餓漢式(枚舉方式)
這種實現方式還沒有被廣泛采用,但這是實現單例模式的最佳方法。它更簡潔,自動支持序列化機制,絕對防止多次實例化,同時這種方式也是《Effective Java 》以及《Java與模式》的作者推薦的方式。
```
public enum Singleton {
//定義一個枚舉的元素,它就是 Singleton 的一個實例
INSTANCE;
public void doSomeThing() {
System.out.println("枚舉方法實現單例");
}
}
```
使用方法:
```
public class ESTest {
public static void main(String[] args) {
Singleton singleton = Singleton.INSTANCE;
singleton.doSomeThing();//output:枚舉方法實現單例
}
}
```
《Effective Java 中文版 第二版》
這種方法在功能上與公有域方法相近,但是它更加簡潔,無償提供了序列化機制,絕對防止多次實例化,即使是在面對復雜序列化或者反射攻擊的時候。雖然這種方法還沒有廣泛采用,但是單元素的枚舉類型已經成為實現Singleton的最佳方法。 —-《Effective Java 中文版 第二版》
《Java與模式》
《Java與模式》中,作者這樣寫道,使用枚舉來實現單實例控制會更加簡潔,而且無償地提供了序列化機制,并由JVM從根本上提供保障,絕對防止多次實例化,是更簡潔、高效、安全的實現單例的方式。
### 2.6 實例場景之 - 獲取jdbc連接
我們新增兩個類,第一個是非單例的獲取jdbc連接
```
package com.mk.designDemo.singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class MDataSourceNoSingle {
private static final Logger logger = LoggerFactory.getLogger(MDataSourceNoSingle.class);
private static Connection connection = null;
public Connection getConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://******:3306/*****?characterEncoding=UTF-8", "root", "root");
logger.info("線程{}實例化connection", Thread.currentThread().getName());
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return connection;
}
}
```
第二個使用了單例模式
```
package com.mk.designDemo.singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class MDataSourceSingle {
private static final Logger logger = LoggerFactory.getLogger(MDataSourceSingle.class);
private static Connection connection = null;
static {
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://******:3306/*****?characterEncoding=UTF-8", "root", "root");
logger.info("線程{}實例化connection", Thread.currentThread().getName());
} catch (ClassNotFoundException | SQLException e) {
logger.error(e.getMessage(), e);
}
}
private MDataSourceSingle() {
}
public static Connection getConnection() {
return connection;
}
}
```
然后我們寫了一個簡單的controller,里面有兩個接口,用來驗證結果
```
package com.mk.designDemo.controller;
import com.mk.designDemo.singleton.MDataSourceNoSingle;
import com.mk.designDemo.singleton.MDataSourceSingle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.sql.Connection;
@RestController
public class SingletonController {
private static final Logger logger = LoggerFactory.getLogger(SingletonController.class);
private final static String sql = "select * from t_trade_date limit 10";
@RequestMapping(path = "/noSingleton")
public String noSingleton() {
MDataSourceNoSingle mDataSourceNoSingle = new MDataSourceNoSingle();
Connection connection = mDataSourceNoSingle.getConnection();
doExecute(connection, sql);
return "OK";
}
@RequestMapping(path = "/singleton")
public String singleton() {
doExecute(MDataSourceSingle.getConnection(), sql);
return "OK";
}
private void doExecute(Connection connection, String sql) {
logger.info("do execute sql:{}", sql);
}
}
```
當我們調用三次非單例的接口時,查看日志輸出如下,connection被實例化了多次:
```
2019-07-09 15:47:31.966 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/noSingleton", parameters={}
2019-07-09 15:47:31.970 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.noSingleton()
2019-07-09 15:47:32.633 [http-nio-80-exec-1] INFO c.m.d.singleton.MDataSourceNoSingle - 線程http-nio-80-exec-1實例化connection
2019-07-09 15:47:32.633 [http-nio-80-exec-1] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:47:32.671 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:47:32.672 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:47:32.693 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK
2019-07-09 15:47:40.005 [http-nio-80-exec-4] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/noSingleton", parameters={}
2019-07-09 15:47:40.007 [http-nio-80-exec-4] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.noSingleton()
2019-07-09 15:47:40.361 [http-nio-80-exec-4] INFO c.m.d.singleton.MDataSourceNoSingle - 線程http-nio-80-exec-4實例化connection
2019-07-09 15:47:40.361 [http-nio-80-exec-4] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:47:40.362 [http-nio-80-exec-4] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:47:40.362 [http-nio-80-exec-4] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:47:40.363 [http-nio-80-exec-4] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK
2019-07-09 15:47:48.459 [http-nio-80-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/noSingleton", parameters={}
2019-07-09 15:47:48.461 [http-nio-80-exec-7] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.noSingleton()
2019-07-09 15:47:48.801 [http-nio-80-exec-7] INFO c.m.d.singleton.MDataSourceNoSingle - 線程http-nio-80-exec-7實例化connection
2019-07-09 15:47:48.801 [http-nio-80-exec-7] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:47:48.802 [http-nio-80-exec-7] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:47:48.802 [http-nio-80-exec-7] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:47:48.803 [http-nio-80-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK
```
然后我們調用單例的接口三次,日志輸出如下,我們可以看到,只有第一次進行了實例化,后面再也沒有繼續實例化
```
2019-07-09 15:49:22.868 [http-nio-80-exec-10] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/singleton", parameters={}
2019-07-09 15:49:22.871 [http-nio-80-exec-10] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.singleton()
2019-07-09 15:49:23.234 [http-nio-80-exec-10] INFO c.m.d.singleton.MDataSourceSingle - 線程http-nio-80-exec-10實例化connection
2019-07-09 15:49:23.235 [http-nio-80-exec-10] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:49:23.237 [http-nio-80-exec-10] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:49:23.237 [http-nio-80-exec-10] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:49:23.239 [http-nio-80-exec-10] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK
2019-07-09 15:49:25.032 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/singleton", parameters={}
2019-07-09 15:49:25.034 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.singleton()
2019-07-09 15:49:25.034 [http-nio-80-exec-1] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:49:25.035 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:49:25.036 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:49:25.038 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK
2019-07-09 15:49:25.926 [http-nio-80-exec-2] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/singleton", parameters={}
2019-07-09 15:49:25.926 [http-nio-80-exec-2] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.singleton()
2019-07-09 15:49:25.927 [http-nio-80-exec-2] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:49:25.928 [http-nio-80-exec-2] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:49:25.928 [http-nio-80-exec-2] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:49:25.929 [http-nio-80-exec-2] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK
```
驗證通過,OVER!
### 2.7 總結
我們主要介紹到了以下幾種方式實現單例模式:
* 餓漢方式(線程安全)
* 懶漢式(非線程安全和synchronized關鍵字線程安全版本)
* 懶漢式(雙重檢查加鎖版本)
* 懶漢式(登記式/靜態內部類方式)
* 餓漢式(枚舉方式)
參考:
《Head First 設計模式》
《Effective Java 中文版 第二版》
[【Java】設計模式:深入理解單例模式](https://itimetraveler.github.io/2016/09/08/%E3%80%90Java%E3%80%91%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%9A%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/#6、枚舉)
- 一.JVM
- 1.1 java代碼是怎么運行的
- 1.2 JVM的內存區域
- 1.3 JVM運行時內存
- 1.4 JVM內存分配策略
- 1.5 JVM類加載機制與對象的生命周期
- 1.6 常用的垃圾回收算法
- 1.7 JVM垃圾收集器
- 1.8 CMS垃圾收集器
- 1.9 G1垃圾收集器
- 2.面試相關文章
- 2.1 可能是把Java內存區域講得最清楚的一篇文章
- 2.0 GC調優參數
- 2.1GC排查系列
- 2.2 內存泄漏和內存溢出
- 2.2.3 深入理解JVM-hotspot虛擬機對象探秘
- 1.10 并發的可達性分析相關問題
- 二.Java集合架構
- 1.ArrayList深入源碼分析
- 2.Vector深入源碼分析
- 3.LinkedList深入源碼分析
- 4.HashMap深入源碼分析
- 5.ConcurrentHashMap深入源碼分析
- 6.HashSet,LinkedHashSet 和 LinkedHashMap
- 7.容器中的設計模式
- 8.集合架構之面試指南
- 9.TreeSet和TreeMap
- 三.Java基礎
- 1.基礎概念
- 1.1 Java程序初始化的順序是怎么樣的
- 1.2 Java和C++的區別
- 1.3 反射
- 1.4 注解
- 1.5 泛型
- 1.6 字節與字符的區別以及訪問修飾符
- 1.7 深拷貝與淺拷貝
- 1.8 字符串常量池
- 2.面向對象
- 3.關鍵字
- 4.基本數據類型與運算
- 5.字符串與數組
- 6.異常處理
- 7.Object 通用方法
- 8.Java8
- 8.1 Java 8 Tutorial
- 8.2 Java 8 數據流(Stream)
- 8.3 Java 8 并發教程:線程和執行器
- 8.4 Java 8 并發教程:同步和鎖
- 8.5 Java 8 并發教程:原子變量和 ConcurrentMap
- 8.6 Java 8 API 示例:字符串、數值、算術和文件
- 8.7 在 Java 8 中避免 Null 檢查
- 8.8 使用 Intellij IDEA 解決 Java 8 的數據流問題
- 四.Java 并發編程
- 1.線程的實現/創建
- 2.線程生命周期/狀態轉換
- 3.線程池
- 4.線程中的協作、中斷
- 5.Java鎖
- 5.1 樂觀鎖、悲觀鎖和自旋鎖
- 5.2 Synchronized
- 5.3 ReentrantLock
- 5.4 公平鎖和非公平鎖
- 5.3.1 說說ReentrantLock的實現原理,以及ReentrantLock的核心源碼是如何實現的?
- 5.5 鎖優化和升級
- 6.多線程的上下文切換
- 7.死鎖的產生和解決
- 8.J.U.C(java.util.concurrent)
- 0.簡化版(快速復習用)
- 9.鎖優化
- 10.Java 內存模型(JMM)
- 11.ThreadLocal詳解
- 12 CAS
- 13.AQS
- 0.ArrayBlockingQueue和LinkedBlockingQueue的實現原理
- 1.DelayQueue的實現原理
- 14.Thread.join()實現原理
- 15.PriorityQueue 的特性和原理
- 16.CyclicBarrier的實際使用場景
- 五.Java I/O NIO
- 1.I/O模型簡述
- 2.Java NIO之緩沖區
- 3.JAVA NIO之文件通道
- 4.Java NIO之套接字通道
- 5.Java NIO之選擇器
- 6.基于 Java NIO 實現簡單的 HTTP 服務器
- 7.BIO-NIO-AIO
- 8.netty(一)
- 9.NIO面試題
- 六.Java設計模式
- 1.單例模式
- 2.策略模式
- 3.模板方法
- 4.適配器模式
- 5.簡單工廠
- 6.門面模式
- 7.代理模式
- 七.數據結構和算法
- 1.什么是紅黑樹
- 2.二叉樹
- 2.1 二叉樹的前序、中序、后序遍歷
- 3.排序算法匯總
- 4.java實現鏈表及鏈表的重用操作
- 4.1算法題-鏈表反轉
- 5.圖的概述
- 6.常見的幾道字符串算法題
- 7.幾道常見的鏈表算法題
- 8.leetcode常見算法題1
- 9.LRU緩存策略
- 10.二進制及位運算
- 10.1.二進制和十進制轉換
- 10.2.位運算
- 11.常見鏈表算法題
- 12.算法好文推薦
- 13.跳表
- 八.Spring 全家桶
- 1.Spring IOC
- 2.Spring AOP
- 3.Spring 事務管理
- 4.SpringMVC 運行流程和手動實現
- 0.Spring 核心技術
- 5.spring如何解決循環依賴問題
- 6.springboot自動裝配原理
- 7.Spring中的循環依賴解決機制中,為什么要三級緩存,用二級緩存不夠嗎
- 8.beanFactory和factoryBean有什么區別
- 九.數據庫
- 1.mybatis
- 1.1 MyBatis-# 與 $ 區別以及 sql 預編譯
- Mybatis系列1-Configuration
- Mybatis系列2-SQL執行過程
- Mybatis系列3-之SqlSession
- Mybatis系列4-之Executor
- Mybatis系列5-StatementHandler
- Mybatis系列6-MappedStatement
- Mybatis系列7-參數設置揭秘(ParameterHandler)
- Mybatis系列8-緩存機制
- 2.淺談聚簇索引和非聚簇索引的區別
- 3.mysql 證明為什么用limit時,offset很大會影響性能
- 4.MySQL中的索引
- 5.數據庫索引2
- 6.面試題收集
- 7.MySQL行鎖、表鎖、間隙鎖詳解
- 8.數據庫MVCC詳解
- 9.一條SQL查詢語句是如何執行的
- 10.MySQL 的 crash-safe 原理解析
- 11.MySQL 性能優化神器 Explain 使用分析
- 12.mysql中,一條update語句執行的過程是怎么樣的?期間用到了mysql的哪些log,分別有什么作用
- 十.Redis
- 0.快速復習回顧Redis
- 1.通俗易懂的Redis數據結構基礎教程
- 2.分布式鎖(一)
- 3.分布式鎖(二)
- 4.延時隊列
- 5.位圖Bitmaps
- 6.Bitmaps(位圖)的使用
- 7.Scan
- 8.redis緩存雪崩、緩存擊穿、緩存穿透
- 9.Redis為什么是單線程、及高并發快的3大原因詳解
- 10.布隆過濾器你值得擁有的開發利器
- 11.Redis哨兵、復制、集群的設計原理與區別
- 12.redis的IO多路復用
- 13.相關redis面試題
- 14.redis集群
- 十一.中間件
- 1.RabbitMQ
- 1.1 RabbitMQ實戰,hello world
- 1.2 RabbitMQ 實戰,工作隊列
- 1.3 RabbitMQ 實戰, 發布訂閱
- 1.4 RabbitMQ 實戰,路由
- 1.5 RabbitMQ 實戰,主題
- 1.6 Spring AMQP 的 AMQP 抽象
- 1.7 Spring AMQP 實戰 – 整合 RabbitMQ 發送郵件
- 1.8 RabbitMQ 的消息持久化與 Spring AMQP 的實現剖析
- 1.9 RabbitMQ必備核心知識
- 2.RocketMQ 的幾個簡單問題與答案
- 2.Kafka
- 2.1 kafka 基礎概念和術語
- 2.2 Kafka的重平衡(Rebalance)
- 2.3.kafka日志機制
- 2.4 kafka是pull還是push的方式傳遞消息的?
- 2.5 Kafka的數據處理流程
- 2.6 Kafka的腦裂預防和處理機制
- 2.7 Kafka中partition副本的Leader選舉機制
- 2.8 如果Leader掛了的時候,follower沒來得及同步,是否會出現數據不一致
- 2.9 kafka的partition副本是否會出現腦裂情況
- 十二.Zookeeper
- 0.什么是Zookeeper(漫畫)
- 1.使用docker安裝Zookeeper偽集群
- 3.ZooKeeper-Plus
- 4.zk實現分布式鎖
- 5.ZooKeeper之Watcher機制
- 6.Zookeeper之選舉及數據一致性
- 十三.計算機網絡
- 1.進制轉換:二進制、八進制、十六進制、十進制之間的轉換
- 2.位運算
- 3.計算機網絡面試題匯總1
- 十四.Docker
- 100.面試題收集合集
- 1.美團面試常見問題總結
- 2.b站部分面試題
- 3.比心面試題
- 4.騰訊面試題
- 5.哈羅部分面試
- 6.筆記
- 十五.Storm
- 1.Storm和流處理簡介
- 2.Storm 核心概念詳解
- 3.Storm 單機版本環境搭建
- 4.Storm 集群環境搭建
- 5.Storm 編程模型詳解
- 6.Storm 項目三種打包方式對比分析
- 7.Storm 集成 Redis 詳解
- 8.Storm 集成 HDFS 和 HBase
- 9.Storm 集成 Kafka
- 十六.Elasticsearch
- 1.初識ElasticSearch
- 2.文檔基本CRUD、集群健康檢查
- 3.shard&replica
- 4.document核心元數據解析及ES的并發控制
- 5.document的批量操作及數據路由原理
- 6.倒排索引
- 十七.分布式相關
- 1.分布式事務解決方案一網打盡
- 2.關于xxx怎么保證高可用的問題
- 3.一致性hash原理與實現
- 4.微服務注冊中心 Nacos 比 Eureka的優勢
- 5.Raft 協議算法
- 6.為什么微服務架構中需要網關
- 0.CAP與BASE理論
- 十八.Dubbo
- 1.快速掌握Dubbo常規應用
- 2.Dubbo應用進階
- 3.Dubbo調用模塊詳解
- 4.Dubbo調用模塊源碼分析
- 6.Dubbo協議模塊