单例设计模式
定义
所谓单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类智能存在一个对象对象实例,并且该类只提供一个取得其对象的方法(静态的)
单例模式的八种设计方式
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
饿汉式(静态常量)
public class Test01 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
// 饿汉式(静态变量)
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.在本类内部创建对选哪个实例
private final static Singleton singleton = new Singleton();
// 3.对外提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return singleton;
}
}
优点:
写法简单,在类装载的时候就进行了实例化,避免了线程同步的问题
缺点:
在类装载的时候完成实例化,类装载的情况很多,不能保证一定会用到这个对象,可能会造成内存的浪费。
总结:
可以使用,但是不是最优方式。
饿汉式(静态代码块)
public class Test {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
// 饿汉式(静态代码块)
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 在静态代码块中创建单例对象
static {
singleton = new Singleton();
}
// 2.在本类内部创建对选哪个实例
private static Singleton singleton;
// 3.对外提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return singleton;
}
}
优点:
和静态常量的方式类似,只不过将创建实例化对象的过程放在了静态代码块中执行,同样是写法简单切避免了线程同步的安全。
缺点:
同样是可能造成内存浪费。
总结:
可以使用,但是不是最优方式
懒汉式(线程不安全)
public class Test {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
class Singleton {
// 私有化构造器
private Singleton() {
}
// 创建一个singleton的属性,但是不创建实例化对象
private static Singleton singleton;
// 提供一个静态的公有方法,在使用该方法时才去创建实例化对象
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
优点:
实现了懒加载的机制,解决了可能的内存浪费。
缺点:
是线程不安全的,只能在单线程情况下使用。当两个线程同时进行到判断对象是否创建时,可能会造成创建多个实例化对象。违背了单例模式的设计初衷。
总结:
在实际开发时不可以使用。
懒汉式(线程安全)
public class Test {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
// 懒汉式(线程安全的)
class Singleton {
// 私有化构造器
private Singleton() {
}
private static Singleton singleton;
public synchronized static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
优点:
解决了线程同步不安全的问题。
缺点:
但是会降低性能,因为在项目中只需要进行一次实例化即可,如果在返回实例化对象处加锁,则会使其在每次都进行线程不同步,大大消耗了性能。
总结:
可以使用,线程是安全的,也可以进行多线程访问,但是性能不好,不推荐。
懒汉式(同步代码块)
public class Test {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
// 懒汉式(线程不安全)
class Singleton {
// 私有化构造器
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized(Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}
结论:
这个方式同样是不能解决线程安全的问题,不可以使用。
双重检查
public class Test {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
// 双重检查
class Singleton {
// 私有化构造器
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized(Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
优点:
可以解决多线程并发的安全问题。
可以实现懒加载,解决可能出现的内存浪费。
同时保证了效率,不会每次都是同步的。
每一次判断实例化是否创建,之后一个线程对类进行锁定,之后在判断对象实例化是否创建,如果第二次判断时还是没有创建,则进行实例化创建。
结论:
在实际开发中推荐使用。
静态内部类
前提:
1) 在外部类进行装载的时候静态内部类不会进行装载
2) 静态内部类装载的时候是线程安全的
public class Test {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
// 静态内部类 完成单例模式
class Singleton {
// 私有化构造器
private Singleton() {
}
private static Singleton singleton;
// 一个静态内部类,该类中有一个静态属性Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
// 提供一个静态的公有方法,直接返回静态内部类中的实例化对象属性
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
优点:
1)这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
2)静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getinstance方法, 才会装载SingletonInstance类,从而完成Singleton的实例化。
3)类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
4)避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
总结:
推荐使用.
枚举方式
public class Test {
public static void main(String[] args) {
Singleton singleton1 = Singleton.INSTANCE;
Singleton singleton2 = Singleton.INSTANCE;
System.out.println(singleton1 == singleton2);
singleton1.sayOK();
singleton2.sayOK();
}
}
enum Singleton {
INSTANCE;
public void sayOK() {
System.out.println("ok~");
}
}
优点:
这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
总结:
推荐使用。
注意事项
1)例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
2)当想实例化一一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
3)单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂 等)