单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
举一个小例子,在我们的windows桌面上,我们打开了一个回收站,当我们试图再次打开一个新的回收站时,Windows系统并不会为你弹出一个新的回收站窗口。,也就是说在整个系统运行的过程中,系统只维护一个回收站的实例。这就是一个典型的单例模式运用。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
适用场景:
- 1.需要生成唯一序列的环境
- 2.需要频繁实例化然后销毁的对象。
- 3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 4.方便资源相互通信的环境
一、单例模式的介绍:
- 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
- 主要解决:一个全局使用的类频繁地创建与销毁。
- 何时使用:当您想控制实例数目,节省系统资源的时候。
- 如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
- 关键代码:构造函数是私有的。
二、优缺点:
优点:
- 在内存中只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
- 避免对资源的多重占用
缺点:
- 没有接口,不能继承,与单一原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
三、单例模式的实现方式:
1、懒汉式:
实例在用到的时候才去创建,“比较懒”,用的时候才去检查有没有实例,,如果有则返回,没有则新建。有线程安全和线程不安全两种写法,区别就是synchronized关键字。
public class singletion {
public static void main(String[] args) {
// TODO Auto-generated method stub
mySingletion aMySingletion=mySingletion.function();
aMySingletion.eat();
mySingletion bMySingletion=mySingletion.function();
System.out.println(aMySingletion==bMySingletion);
}
}
class mySingletion{
// 懒汉式
private static mySingletion instance;
private mySingletion() {
//私有构造方法
}
public static mySingletion function() {
//public static synchronized mySingletion function()线程安全
if (instance==null) {
instance=new mySingletion();
}
return instance;
}
public void eat() {
System.out.println("吃了么");
}
}
2、饿汉式:
实例在初始化的时候就已经建好了,不管你有没有用到,都先建好了再说。好处是没有线程安全的问题,坏处是浪费内存空间。
package demoTest;
public class esingletion {
public static void main(String[] args) {
emySingletion aMySingletion=emySingletion.getinstance();
emySingletion bMySingletion=emySingletion.getinstance();
bMySingletion.sleep();
System.out.println(aMySingletion==bMySingletion);
}
}
class emySingletion{
//饿汉式
private static emySingletion instance=new emySingletion();
private emySingletion() {
//私有构造方法
}
public static emySingletion getinstance() {
return instance;
}
public void sleep() {
System.out.println("晚安!!!!!!!!!");
}
}
3、双检锁/双重校验锁:
这种方式采用双锁机制,安全且在多线程情况下能保持高性能,综合了懒汉式和饿汉式两者的优缺点整合而成,在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
4、静态内部类:
这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
5、枚举
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化,既保证了线程安全的需求,又保证了资源不被浪费。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
注:
一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。