设计模式之单例模式
对于单例模式,就是在一个软件系统中该类只允许创建一个对象或者说类的实例,该类负责创建自己的对象,同时确保只有单个对象被创建。那这个类就是一个单例类,这种设计模式叫做单例设计模式,简称单例模式。
单例模式的应用场景:
在JDK中的应用:
在JDK中java.lang.Runtime就是经典的单例模式(饿汉式)
单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象)、但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)
如何实现一个单例
八种方法
实现一个单例需要关注一下几点:
- 构造函数需要是private修饰的,可以避免外部通过new创建实例。
- 创建对象时的线程安全问题。
- 是否支持懒加载。
- getinstance()方法的性能是否高。
饿汉式(静态变量)
//饿汉式(静态变量)
public class singleton01 {
public static void main(String[] args) {
single single = designpattern.single.getInstance();
single single2 = designpattern.single.getInstance();
System.out.println(single == single2);
System.out.println(single.hashCode());
System.out.println(single2.hashCode());
}
}
class single {
private static final single instance = new single();
private single() {
}
public static single getInstance() {
return instance;
}
}
饿汉式的实现,在类加载的时候,instance静态实例就已经被创建并初始化了,因为类加载的时候是线程安全的,所以该方法是线程安全的,不过饿汉式的实现方式不支持懒加载,所谓懒加载就是在程序系统启动之后,用到了某个实例再创建这个实例。如果从始至终从未使用过这个实例,就是造成内存的浪费。
该方法可用,但可能会造成内存浪费。
饿汉式(静态代码块)
//饿汉式(静态代码块)
public class singleton02 {
public static void main(String[] args) {
single02 single = single02.getInstance();
single02 single2 = single02.getInstance();
System.out.println(single == single2);
System.out.println(single.hashCode());
System.out.println(single2.hashCode());
}
}
class single02 {
private static single02 instance;
private single02() {
}
static {
instance = new single02();
}
public static single02 getInstance() {
return instance;
}
}
这种方式将类实例化的过程放在了静态代码块中,在类加载的时候就执行静态代码块中的代码。
该方法可用,但可能会造成内存浪费。
懒汉式(线程不安全)
public class singleton03 {
public static void main(String[] args) {
single03 single = single03.getInstance();
single03 single2 = single03.getInstance();
System.out.println(single == single2);
System.out.println(single.hashCode());
System.out.println(single2.hashCode());
}
}
class single03 {
private static single03 instance;
private single03() {
}
public static single03 getInstance() {
if (instance == null) {
instance = new single03();
}
return instance;
}
}
懒汉式使用了懒加载,但是这种方式只能在单线程下使用,多线程下是不安全的,可能会创建多个实例对象。该方法不推荐使用。
懒汉式(线程安全,同步方法)
public class singleton04 {
public static void main(String[] args) {
single04 single = single04.getInstance();
single04 single2 = single04.getInstance();
System.out.println(single == single2);
System.out.println(single.hashCode());
System.out.println(single2.hashCode());
}
}
class single04 {
private static single04 instance;
private single04() {
}
public static synchronized single04 getInstance() {
if (instance == null) {
instance = new single04();
}
return instance;
}
}
使用synchronized将getInstance()方法标记为同步方法,是该方法在多线程下线程安全,但是
这样会是程序的性能降低,多线程情况下,虽然安全了,但是效率降低,当对象实例化之后,仍然需要加锁,后面的线程想要获得对象直接return就可以了,因此该方法不推荐使用。
懒汉式(线程安全,同步代码块)
public class singleton05 {
public static void main(String[] args) {
single05 single = single05.getInstance();
single05 single2 = single05.getInstance();
System.out.println(single == single2);
System.out.println(single.hashCode());
System.out.println(single2.hashCode());
}
}
class single05 {
private static single05 instance;
private single05() {
}
public static single05 getInstance() {
if (instance == null) {
synchronized (single05.class) {
instance = new single05();
}
}
return instance;
}
}
多线程下仍然可能会创建多个实例
双重检锁(DCL,Double Check Lock)
public class singleton06 {
public static void main(String[] args) {
single06 single = single06.getInstance();
single06 single2 = single06.getInstance();
System.out.println(single == single2);
System.out.println(single.hashCode());
System.out.println(single2.hashCode());
}
}
class single06 {
private static single06 instance;
private single06() {
}
public static single06 getInstance() {
if (instance == null) {
synchronized (single06.class) {
if (instance == null)
instance = new single06();
}
}
return instance;
}
}
在getInstance()方法中使用到了两个if判断语句,这样是为了避免多线程下因为线程切换导致创建多个实例的情况。
线程安全,延迟加载且效率较高。
静态内部类
public class singleton06 {
public static void main(String[] args) {
single06 single = single06.getInstance();
single06 single2 = single06.getInstance();
System.out.println(single == single2);
System.out.println(single.hashCode());
System.out.println(single2.hashCode());
}
}
class single06 {
private single06() {
}
private static class singleholder {
private static final single06 INSTANCE = new single06();
}
public static single06 getInstance() {
return singleholder.INSTANCE;
}
}
采用类加载的机制类保证初始化实例时只有一个线程。
静态内部类在singleton类加载时不会实例化,当调用getInstance()方法时,因为需要返回内部类的变量,所以需要对内部类进行加载,从而完成实例化。
推荐使用该方法。
枚举类
public class singleton08 {
public static void main(String[] args) {
single08 single = single08.INSTANCE;
single08 single2 = single08.INSTANCE;
System.out.println(single == single2);
}
}
enum single08 {
INSTANCE;
}
使用枚举类可以避免多线程同步问题,还可以防止反序列化重新创建新的对象。
推荐使用。