[TOC]
## 1. 單例模式
**餓漢**由jvm保證單例性:類屬性只有在類加載的時候保證一次初始化(**編譯時,將類方法收集到clinit<>方法中,clinit<>方法保證百分之百的同步,滿足可見、原子、順序性**)
**懶漢:**需要收到加synchronized進行同步
### 1.1 餓漢模式
> * 餓漢模式
優點是:寫起來比較簡單,而且不存在多線程同步問題,避免了synchronized所造成的性能問題;
缺點是:當類SingletonTest被加載的時候,會初始化static的instance,靜態變量被創建并分配內存空間,從這以后,這個static的instance對象便一直占著這段內存(即便你還沒有用到這個實例),當類被卸載時,靜態變量被摧毀,并釋放所占有的內存,因此在某些特定條件下會耗費內存。
~~~
/**
* 方法一
* 單例模式的實現:餓漢式,線程安全 但效率比較低
*/
public class SingletonTest {
// 1. 定義一個私有的構造方法
private SingletonTest() {
}
// 2. 將自身的實例對象設置為一個屬性,并加上Static和final修飾符
private static final SingletonTest instance = new SingletonTest();
// 3. 靜態方法返回該類的實例
public static SingletonTest getInstancei() {
return instance;
}
}
~~~
* * * * *
### 1.2 飽漢模式
~~~
/**
*
* 單例模式的實現:飽漢式,線程安全簡單實現
*
*/
public class SingletonTest {
// 定義一個私有構造方法
private SingletonTest() {
}
//定義一個靜態私有變量(不初始化,不使用final關鍵字,使用volatile保證了多線程訪問時instance變量的可見性,避免了instance初始化時其他變量屬性還沒賦值完時,被另外線程調用)
private static volatile SingletonTest instance;
//定義一個共有的靜態方法,返回該類型實例
public static SingletonTest getIstance() {
// 對象實例化時與否判斷(不使用同步代碼塊,instance不等于null時,直接返回對象,提高運行效率)
if (instance == null) {
//同步代碼塊(對象未初始化時,使用同步代碼塊,保證多線程訪問時對象在第一次創建后,不再重復被創建)
synchronized (SingletonTest.class) {
//未初始化,則初始instance變量
if (instance == null) {
instance = new SingletonTest();
}
}
}
return instance;
}
}
~~~
### 1.3 靜態內部類創建單例對象(推薦)
通過將這個單實例的引用變量定義在靜態內部類中,來實現單例,這樣可以做到不用if條件進行判斷,并且是多線程安全的(由jvm保證)。
1.類加載的最有初始化過程會調用類對象<clinit>構造方法
2. <clinit>**靜態變量、常量的賦值動作和靜態代碼塊** 組成的
3. jvm保證多線程下同步加鎖clinit方法,保障方法執行一次
靜態內部類在用到時,加載到內存,到了初始化階段執行clinit方法(僅一次,保證單例),執行其中的靜態屬性賦值(工具類初始化)
~~~
package JavaTest.SingletonTest;
/**
* Created by dailin on 2017/11/23.
*/
public class SingleTest {
//定義一個內部類
private static class NestClass {
private static SingleTest instance;
//利用靜態代碼塊,對外圍類初始化
static {
System.out.println("靜態內部類NestClass靜態代碼塊執行。。。。。");
System.out.println("instance = new SingletonTest()。。。。。。");
instance = new SingleTest();
}
}
// 不能直接new
private SingleTest() {
System.out.println("private SingletonTest()");
}
public static SingleTest getInstance() {
System.out.println("SingletonTest getInstance()");
return NestClass.instance;
}
public static void main(String[] args) {
SingleTest instance = SingleTest.getInstance();
System.out.println("========================================");
SingleTest instance01 = SingleTest.getInstance();
System.out.println("========================================");
SingleTest instance02 = SingleTest.getInstance();
System.out.println(instance01 == instance);
}
}
~~~
例2:通過內部類實現 KeyStore 對象的單例
~~~
package cn.com.bigssl.crypto;
import com.aexit.motordriver.commons.utils.TimingDBConnection;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
public class TimingCertificate {
private PrivateKey privateKey;
private String SerialNumber;
// 直接Nested.keyStore使用
static class Nested{
static KeyStore keyStore = null;
static String passwd = "1";
static {
String osType = System.getProperty("os.name");
String path = System.getProperty("user.home") + "/application/jilinax.pfx";
try {
keyStore = KeyStore.getInstance("PKCS12");
InputStream input = new FileInputStream(path);
keyStore.load(input, passwd.toCharArray());
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void readPfx() throws Exception {
Enumeration<String> aliases = Nested.keyStore.aliases();
if (!aliases.hasMoreElements())
throw new RuntimeException("no alias found");
String alias = aliases.nextElement();
X509Certificate cert = (X509Certificate) Nested.keyStore.getCertificate(alias);
SerialNumber = cert.getSerialNumber().toString();//序列號
SerialNumber = Long.toHexString(Long.parseLong(SerialNumber)).toUpperCase();
privateKey = (PrivateKey) Nested.keyStore.getKey(alias, Nested.passwd.toCharArray());//私鑰
}
public PrivateKey getPrivateKey() {
return privateKey;
}
public String getSerialNumber() {
return SerialNumber;
}
public static void main(String[] args){
System.out.println(System.getProperty("user.home"));
}
}
~~~
例3:測試單例
~~~
package com.aixin.tuna.fdfs;
import org.csource.fastdfs.*;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Created by dailin on 2018/7/11.
*/
public class FdfsUtil {
static class Nested {
private static TrackerServer trackerServer =null;
private static StorageServer storageServer = null;
private static StorageClient storageClient = null;
static {
try {
ClientGlobal.init("fdfs_client.conf");
TrackerClient tracker = new TrackerClient();
trackerServer = tracker.getConnection();
storageClient = new StorageClient(trackerServer, storageServer);
}catch (Exception e) {
e.printStackTrace();
}
}
}
//獲取單例
public static StorageClient getStorageClient() {
return Nested.storageClient;
}
}
~~~
~~~
import com.aixin.tuna.fdfs.FdfsUtil;
import org.csource.fastdfs.StorageClient;
import org.junit.Test;
import javax.sound.midi.Soundbank;
/**
* Created by dailin on 2018/7/11.
*/
public class SingleInstanceTest {
@Test
public void singleInstanceTest() {
StorageClient storageClient;
StorageClient storageClient1;
storageClient = FdfsUtil.getStorageClient();
storageClient1 = FdfsUtil.getStorageClient();
System.out.println(storageClient == storageClient1);
int storageClientAddr = System.identityHashCode(storageClient);
int storageClientAddr1 = System.identityHashCode(storageClient1);
System.out.println("storageClient地址:" + storageClientAddr);
System.out.println("storageClient1地址:" + storageClientAddr1);
}
}
~~~
~~~
true
storageClient地址:1347137144
storageClient1地址:1347137144
~~~
### 1.4 使用場景
適用場景:
1.需要生成唯一序列的環境
2.需要頻繁實例化然后銷毀的對象。
3.創建對象時耗時過多或者耗資源過多,但又經常用到的對象。
4.方便資源相互通信的環境
- 計算機網絡
- 基礎_01
- tcp/ip
- http轉https
- Let's Encrypt免費ssl證書(基于haproxy負載)
- what's the http?
- 網關
- 網絡IO
- http
- 工具
- Git
- 初始本地倉庫并上傳
- git保存密碼
- Gitflow
- maven
- 1.生命周期命令
- 聚合與繼承
- 插件管理
- assembly
- 資源管理插件
- 依賴范圍
- 分環境打包
- dependencyManagement
- 版本分類
- 找不到主類
- 無法加載主類
- 私服
- svn
- gradle
- 手動引入第三方jar包
- 打包exe文件
- Windows
- java
- 設計模式
- 七大原則
- 1.開閉原則
- 2. 里式替換原則
- 3. 依賴倒置原則
- 4. 單一職責原則
- 單例模式
- 工廠模式
- 簡單工廠
- 工廠方法模式
- 抽象工廠模式
- 觀察者模式
- 適配器模式
- 建造者模式
- 代理模式
- 適配器模式
- 命令模式
- json
- jackson
- poi
- excel
- easy-poi
- 規則
- 模板
- 合并單元格
- word
- 讀取
- java基礎
- 類路徑與jar
- 訪問控制權限
- 類加載
- 注解
- 異常處理
- String不可變
- 跨域
- transient關鍵字
- 二進制編碼
- 泛型1
- 與或非
- final詳解
- Java -jar
- 正則
- 讀取jar
- map
- map計算
- hashcode計算原理
- 枚舉
- 序列化
- URLClassLoader
- 環境變量和系統變量
- java高級
- java8
- 1.Lambda表達式和函數式接口
- 2.接口的默認方法和靜態方法
- 3.方法引用
- 4.重復注解
- 5.類型推斷
- 6.拓寬注解的應用場景
- java7-自動關閉資源機制
- 泛型
- stream
- 時區的正確理解
- StringJoiner字符串拼接
- 注解
- @RequestParam和@RequestBody的區別
- 多線程
- 概念
- 線程實現方法
- 守護線程
- 線程阻塞
- 筆試題
- 類加載
- FutureTask和Future
- 線程池
- 同步與異步
- 高效簡潔的代碼
- IO
- ThreadLocal
- IO
- NIO
- 圖片操作
- KeyTool生成證書
- 壓縮圖片
- restful
- 分布式session
- app保持session
- ClassLoader.getResources 能搜索到的資源路徑
- java開發規范
- jvm
- 高并發
- netty
- 多線程與多路復用
- 異步與事件驅動
- 五種IO模型
- copy on write
- code style
- 布隆過濾器
- 筆試
- 數據庫
- mybatis
- mybatis與springboot整合配置
- pagehelper
- 分頁數據重復問題
- Java與數據庫之間映射
- 攔截器
- 攔截器應用
- jvm
- 堆內存測試
- 線程棧
- 直接內存
- 內存結構
- 內存模型
- 垃圾回收
- 調優
- 符號引用
- 運行參數
- 方法區
- 分帶回收理論
- 快捷開發
- idea插件
- 注釋模板
- git
- pull沖突
- push沖突
- Excel處理
- 圖片處理
- 合并單元格
- easypoi
- 模板處理
- 響應式編程
- reactor
- reactor基礎
- jingyan
- 規范
- 數據庫