单例模式的简单理解
Posted CppTeam
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式的简单理解相关的知识,希望对你有一定的参考价值。
单例模式
定义:在应用单例模式时,一个类只能有一个对象。这个对象只能自行创建,可以全局有效。
-
饿汉式单例 -
懒汉式单例 -
静态内部类 -
枚举方式
饿汉式单例
饿汉式单例,顾名思义。类加载时就创建一个实例,调用方法时提供实例。它的线程安全由JVM提供。缺点就是初始化时,可能会因为要分配多个实例,占用较多的资源。
上代码
/**
* 饿汉式单例
*/
public class Hungry {
//可能浪费空间
private byte[] data1 =new byte[1024*1024];
private byte[] data2 =new byte[1024*1024];
// 构造反法私有化,不允许外部访问,确保自行创建实例
private Hungry(){
}
// 创建唯一的静态实例,确保唯一性
private final static Hungry HUNGRY =new Hungry();
// 提供一个获取实例的公有方法,确保全局有效
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式单例
懒汉式单例,顾名思义,比较“懒”,延迟加载,类记载时不创建实例,而是先声明一个实例,调用方法时提供实例
/**
* 懒汉式单例
*/
public class LazyMan {
private LazyMan(){
synchronized (LazyMan.class){ //相当于是第三层锁
if (lazyMan!=null){
throw new RuntimeException("不要试图使用反射破化单例");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
/**双重检测锁+原子性操作
*
*/
//声明一个实例
private volatile static LazyMan lazyMan; //第二层锁 //volatile 修饰符阻止了变量访问前后的指令重排,保证指令的执行顺序
public static LazyMan getInstance(){
if (lazyMan==null){ //第一层检测
synchronized (LazyMan.class){ //第一层锁
if (lazyMan==null){ //第二层检测
lazyMan=new LazyMan(); //不是一个原子操作
/**
* instance = new DoubleCheckIdGenerator(); 并不是一个原子操作,
* 1. 分配内存空间
* 2. 执行构造方法
* 3. 把这个对象指向这个空间
* 在创建的过程中,因为JVM的优化,2和3这两个步骤是可以发生指令重排的,这就导致了多线程情况下,会返回没经过初始化的对象,加个volatile既可以解决这个问题
*/
}
}
}
return lazyMan;
}
整个代码的实现思路:先声明一个带volatile修饰符的实例,当这个实例是空的时候,给这个实例上把锁 ,保证只有一个线程进入。当第一个线程结束后可能还会由进程进来,创建多个对象,造成线程不安全,所以要进行第二次检测。
静态内部类
静态内部类思想的精妙之处在于“静态内部类”,什么意思呢?
静态内部类定义的实例属于类,而不是属于某个对象。当加载外部类Holder时不会创建内部类InnerClass的实例对象,只有调用getInstance()方法时才会创建HOLDEE实例对象。HOLDER 的线程安全由JVM保证。
/**
* 静态内部类
*/
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER=new Holder();
}
}
枚举方式
枚举方式实现的单例模式不仅能避免多线程同步的问题,也可以防止反射的破坏。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 枚举类型
* enum是什么。本身也是一个class类
*/
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance() {
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException,IllegalAccessException, InvocationTargetException,InstantiationException {
EnumSingle instance = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance1 = declaredConstructor.newInstance();
//输出测试
System.out.println(instance);
System.out.println(instance1);
}
}
以上是关于单例模式的简单理解的主要内容,如果未能解决你的问题,请参考以下文章
Java:java学习笔记之Java单例模式的简单理解和使用