详解单例模式
关于单例模式,话不多说,即程序运行时无论New了多少次,即内存中只有一个实例对象。即对象的HasHCode一致。
单例模式的两大类
1、饿汉模式(即加载时就创建对象)
-1、直接实例化饿汉模式
-2、静态代码块饿汉模式(即需要加载初始化配置的时候适用)
-3、枚举方式
2、懒汉式(延迟加载)
-1、单线程安全下的懒汉
-2、多线程安全下的懒汉
-3、静态内部类的懒汉(安全)
1、直接实例化饿汉模式
package com.single;
public class Singleton {
private static final Singleton SI=new Singleton();
private Singleton(){
}
public static Singleton getsingleton(){
return SI;
}
}
单例模式必须保证自行创建,并且内部提供一个静态变量来保存这个唯一的对象,构造器私有化,即其他类内部中无法直接New当前的单例类,还需要提供一个对外获取实例的方法
当前模式在加载类的字节码的时候当前类的实例就立即会被创建
2、静态代码块饿汉模式(即需要加载初始化配置的时候适用)
1 public class Singleton2 { 2 3 private static final Singleton2 SI; 4 5 private String name; 6 static { 7 Properties properties=new Properties(); 8 try { 9 10 properties.load(Singleton2.class.getClassLoader().getResourceAsStream("db.properties")); 11 String name = properties.getProperty("name"); 12 SI=new Singleton2(name); 13 } catch (IOException e) { 14 throw new RuntimeException(e); 15 } 16 17 } 18 19 private Singleton2(String name){ 20 this.name=name; 21 } 22 23 public static Singleton2 getsingleton(){ 24 return SI; 25 }
当我们需要在类创建的时候初始化参数,并且加载某些配置文件的时候可以使用 静态代码块的方式,在创建实例的时候通过构造器来完成相关参数的初始化。
3.枚举方式的单例,自JDK1.5之后,首先枚举类和普通类的区别是什么?
使用enum定义的枚举类默认继承了java.lang.Enum类
枚举类的构造器只能使用private
枚举类的每个实例必须在枚举类中显示的列出(,分隔 ;结尾) 列出的实例系统会自动添加public static final修饰
所有的枚举类都定义了一个values方法,该方法可以很方便的遍历所有的枚举值
可以在switch表达式使用枚举类对象作为表达式,case子句可以直接使用枚举的名字,无需添加枚举类作为限定
枚举类对象的属性不能更改,所以要用private final修饰
枚举类对象要在构造器中被赋值
---------------------
作者:weirdowang
来源:CSDN
原文:https://blog.csdn.net/weirdowang/article/details/79970673
package com.single;
/*
枚举方式的单例
*/
public enum SingEnum {
SING;
SingEnum(){
}
public void info(){
System.out.println("显示");
}
}
是不是很简洁呢? 确实如此,单例模式下 枚举方式的单例是最简洁的
二、懒汉模式
1、单线程安全下的懒汉
package com.single2;
/*
懒汉模式
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
System.out.println("创建");
}
public static Singleton getSingleton(){
if (singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
我们可以看出无论怎么样,当前的懒汉都是在调用的时候才被加载创建的,但是它只是在单线程的情况下是安全的为什么呢? 现在有一个需求即多个线程对这个 单例进行访问构建对象,在构建对象的时候使当前线程短暂的休眠一下
package com.single2;
/*
懒汉模式测试线程不安全
*/
public class Singleton2 {
private static Singleton2 singleton;
private Singleton2(){
System.out.println("创建");
}
public static Singleton2 getSingleton() throws InterruptedException {
if (singleton==null){
Thread.sleep(100);
singleton=new Singleton2();
}
return singleton;
}
}
测试代码如下:
/*
懒汉模式下的多线程不安全
*/
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Singleton2 singleton1 = Singleton2.getSingleton();
System.out.println(singleton1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Singleton2 singleton2 = Singleton2.getSingleton();
System.out.println(singleton2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
thread2.start();
}
经过测试,多线程情况下这种是不安全的 为什么呢? 第一个线程进入 进去之后, 线程休眠100ms 在此期间,第二个线程也开始进去到了创建的方法,由于当前第一个线程正在休眠,所以当前单例为null ,然后第一个线程休眠结束,创建第一个对象,此后第二个线程还在if中,也相继创建对象,此时便构造成了线程不安全的懒汉单例
2、线程安全的懒汉单例(双端检索)
package com.single2;
/*
懒汉模式 双端检索,避免了多次线程等待
*/
public class Singleton4 {
private static Singleton4 singleton4;
private Singleton4() {
}
public static Singleton4 getSingleton4() {
if (singleton4 == null) {
synchronized (Singleton4.class) {
if (singleton4 == null) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton4 = new Singleton4();
}
}
}
return singleton4;
}
}
使用synchronized 保证当前只能有一个线程进入 ,并且在外再次进行判断 若当前的单例已经被创建过了,避免了再次加锁,直接返回
3、静态内部类的懒汉单例:(线程安全的)
package com.single2;
public class Singleton5 {
private Singleton5(){
}
private static class demo{
private static final Singleton5 SINGLETON_5=new Singleton5();
}
public static Singleton5 singleton5(){
return demo.SINGLETON_5;
}
}
也是懒汉模式最简单的单例实现,静态内部类不会随着外部类的初始化而初始化,并且内部类具有自己的类加载器,是安全的
如上便是单例模式的六种创建方式,欢迎大牛指正,源码放到群里了 另外也欢迎各位朋友们一起学习交流, qq群:956809929