[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
- 规范
- 数据库