提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、单例模式是什么?
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
二、简单实现(饿汉式的单例和懒汉式的单例)
1.饿汉式
public class esingle {
private static esingle single =new esingle("张三");
private String name;
private esingle(Str
ing name){
this.name=name;
}
public static esingle getInstance(){
return single;
}
public static void main(String[] args) {
esingle instance = esingle.getInstance();
esingle instance1 = esingle.getInstance();
System.out.println(instance);
System.out.println(instance1);
System.out.println(instance == instance1);
}
}
2.懒汉式
public class esingle {
private static esingle single;
private String name;
private esingle(String name){
this.name=name;
}
public static esingle getInstance(){
if (single==null){
single=new esingle("张三");
}
return single;
}
public static void main(String[] args) {
esingle instance = esingle.getInstance();
esingle instance1 = esingle.getInstance();
System.out.println(instance);
System.out.println(instance1);
System.out.println(instance == instance1);
}
}
以上是单例模式的简单实现,但只适用于单一线程(只在单一线程安全),在多线程下是不安全的,原因可以自己百度一下,这里就不详解了。
三、实现在多线程下看上去安全的单例模式(以懒汉式为例)
代码如下(示例):
public class esingle {
private volatile static esingle single;
private static boolean zrf=false;
private String name;
private esingle(String name){
synchronized (esingle.class){
if (zrf==false){
zrf=true;
this.name=name;
}else{
throw new RuntimeException("实例化失败");
}
}
}
//双重锁检查
public static esingle getInstance(){
if (single==null){
synchronized (esingle.class){
if (single==null){
single=new esingle("张三");
}
}
}
return single;
}
public static void main(String[] args) throws Exception {
//用线程池进行测试
Executor executor=new ThreadPoolExecutor(
5,10,60, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
for (int i = 0; i <100; i++) {
executor.execute(()->{
esingle instance = esingle.getInstance();
System.out.println(instance);
});
}
}
}
这样实现单例在多线程下是绝对安全的,但是如果你接触过java的反射知识就可破坏这种单例模式,
你可以在main方法中执行以下代码
Field zrf = esingle.class.getDeclaredField("zrf");
zrf.setAccessible(true);
Constructor<esingle> declaredConstructor = esingle.class.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);
esingle lis = declaredConstructor.newInstance("李四");
zrf.set(lis,false);
esingle wanwu = declaredConstructor.newInstance("王五");
System.out.println(wanwu);
System.out.println(lis);
System.out.println(wanwu==lis);
执行结果如下
你会惊讶的发现单例被破坏了,那么反射就是无敌的了吗?
请看以下JDK newInstance的源码
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
从中我们可以看出反射是不可以破坏枚举类型的类的(枚举类型天生就是单例的),所有我们可以继续完善我们的代码,
终极单例模式
public enum esingle {
ESINGLE("张三");
private static boolean zrf=false;
private String name;
private esingle(String name){
this.name=name;
}
public synchronized static esingle getInstance(){
return ESINGLE;
}
}
由于枚举类型的特性我们只能使用饿汉式实现,现在这个单例是目前为主最安全的,不仅是多线程安全,而且无法被反射破坏。