目录
一、介绍
什么是单例模式 单例模式确保一个类只有一个实例,并提供一个全局访问点,实现单例模式的方法是私有化构造函数,通过getInstance()方法实例化对象,并返回这个实例 保证在JVM中只有一个实例 幂等 JVM中如何保证实例的幂等问题 保证唯一性 饿汉、懒汉 双重检验 单例模式优缺点 1、单例类只有一个实例 2、共享资源,全局使用 3、节省创建时间,提高性能 缺点:可能存在线程不安全的问题 解决线程安全问题 单例的七种写法 分别是 饿汉、懒汉(非线程安全)、懒汉(线程安全)、双重校验锁、静态内部类、枚举和容器类管理、静态块初始化
二、示例
package com.example.designmode.demo.single;
public class Singleton1 {
/**
* 饿汉式 优点:先天性线程是安全的,当类初始化的 就会创建该对象 缺点:如果饿汉式使用过多,可能会影响项目启动的效率问题。
*/
private static Singleton1 singleton1 = new Singleton1();
/**
* 将构造函数私有化 禁止初始化
*/
private Singleton1() {
}
public static Singleton1 getInstance() {
return singleton1;
}
public static void main(String[] args) {
Singleton1 instance1 = Singleton1.getInstance();
Singleton1 instance2 = Singleton1.getInstance();
System.out.println(instance1 == instance2);
}
}
package com.example.designmode.demo.single;
public class Singleton2 {
/**
* 懒汉式 (线程不安全)
*/
private static Singleton2 singletonV2;
private Singleton2() {
}
/**
* 在真正需要创建对象的时候使用...
*
* @return
*/
public static Singleton2 getInstance() {
if (singletonV2 == null) {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
singletonV2 = new Singleton2();
}
return singletonV2;
}
public static void main(String[] args) {
// 1.模拟线程不安全
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
Singleton2 instance1 = Singleton2.getInstance();
System.out.println(Thread.currentThread().getName() + "," + instance1);
}
}).start();
}
}
}
package com.example.designmode.demo.single;
public class Singleton3 {
/**
* 懒汉式 线程安全
*/
private static Singleton3 singletonV3;
private Singleton3() {
}
/**
* 能够解决线程安全问题,创建和获取实例时都上锁 ,效率非常低,所以推荐使用双重检验锁
*
* @return
*/
public synchronized static Singleton3 getInstance() {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
if (singletonV3 == null) {
System.out.println("创建实例SingletonV3");
singletonV3 = new Singleton3();
}
System.out.println("获取SingletonV3实例");
return singletonV3;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
Singleton3 instance1 = Singleton3.getInstance();
System.out.println(Thread.currentThread().getName() + "," + instance1);
}
}).start();
}
}
}
package com.example.designmode.demo.single;
public class Singleton4 {
/**
* volatile 禁止重排序和 提高可见性
*/
private volatile static Singleton4 singletonV4;
private Singleton4() {
}
/**
* 双重检验锁
*/
public static Singleton4 getInstance() {
if (singletonV4 == null) { // 第一次判断如果没有创建对象 开始上锁...
synchronized (Singleton4.class) {
if (singletonV4 == null) { // 当用户抢到锁,判断初始化
System.out.println("第一次开始创建实例对象....获取锁啦...");
try {
Thread.sleep(2000);
} catch (Exception e) {
}
singletonV4 = new Singleton4();
} else {
System.out.println("对象已经被创建好啦");
}
}
}
return singletonV4;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
Singleton4 instance1 = Singleton4.getInstance();
System.out.println(Thread.currentThread().getName() + "," + instance1);
}
}).start();
}
}
}
package com.example.designmode.demo.single;
public class Singleton5 {
private Singleton5() {
System.out.println("对象初始...");
}
public static Singleton5 getInstance() {
return SingletonV5Utils.singletonV5;
}
/**
* 静态内部方式能够避免同步带来的效率问题和有能实现延迟加载
*/
public static class SingletonV5Utils {
private static Singleton5 singletonV5 = new Singleton5();
}
public static void main(String[] args) {
System.out.println("项目启动成功");
Singleton5 instance1 = Singleton5.getInstance();
Singleton5 instance2 = Singleton5.getInstance();
System.out.println(instance1 == instance2);
}
}
package com.example.designmode.demo.single;
import java.lang.reflect.Constructor;
public enum Singleton6 {
INSTANCE;
// 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解
public void add() {
System.out.println("add方法...");
}
public static void main(String[] args) throws Exception {
Singleton6 instance1 = Singleton6.INSTANCE;
Singleton6 instance2 = Singleton6.INSTANCE;
System.out.println(instance1 == instance2);
Constructor<Singleton6> declaredConstructor = Singleton6.class.getDeclaredConstructor();
//这里会抛异常:java.lang.NoSuchMethodException,枚举类不允许
//通过该错误说明,枚举类中无参数构造函数
//使用java反编译技术,查看枚举类
//在该反编译源码中,定义了一个类继承了Enum 该类是中没有无参构造函数,所以反射机制调用无参构造函数是无法初始化的。
//在该类中有一个只有一个有参构造函数,使用注入有参构造函数是否可以破解枚举呢?也不行的
//主要原因是 java的反射初始化对象中,只要对象是是枚举是不会初始化的的
declaredConstructor.setAccessible(true);
Singleton6 v3 = declaredConstructor.newInstance();
System.out.println(v3==instance1);
}
}
package com.example.designmode.demo.single;
import java.util.HashMap;
import java.util.Map;
public class Singleton7 {
/**
* 这种使用容器管理 将多种单例类统一管理,在使用时根据key获取对象对应类型的对象。
* 这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度
*/
private static Map<String, Object> objMap = new HashMap<String, Object>();
public static void registerService(String key, Object instance) {
if (!objMap.containsKey(key)) {
objMap.put(key, instance);
}
}
public static Object getService(String key) {
{
return objMap.get(key);
}
}
}