就是采取一定的方法,保证在整个软件系统中,对某个类只能存在一个实例对象,并且该类只提供一个获取其对象实例的方法(静态方法).
如果你创建了一个对象, 过一会之后决定再创建一个对象,你就会获得之前已经创建的对象,而不是一个新对象.
所以必须为该实例提供一个全局访问点.
- 在类中添加一个私有的静态成员变量用于保存单例实例
- 将默认的构造函数设为私有,防止其他对象使用单例类的new运算符
- 新建一个静态构建方法作为构造函数,该函数会偷偷调用私有构造函数来穿件对象,并将其保存在一个静态成员变量中,此后所有对于该函数的调用都将返回这一缓存对象
后三种是推荐使用的,饿汉式看情况使用
- 构造器私有化(防止外部new一个对象)
- 类的内部创建对象
- 向外暴露一个静态的公共方法
class Singleton {
//1. 构造器私有化, 外部不能new
private Singleton() {
}
//2.本类内部创建对象实例
//final 在定义时必须初始化,不能被修改
private final static Singleton instance = new Singleton();
//3. 提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
优点 : 写法比较简单,就是在类装载的时候就完成实例化,避免了线程同步问题(类装载的时候是线程安全的)
缺点 : 在类装载的时候就完成实例化,没有达到lazy loading的效果,如果一直没有用过这个实力,则会造成内存浪费
- 这种方式基于类加载的机制,避免了多线程的同步问题,不过,instance在类装载时就实例化,没有达到lazy loading的效果
这种单例可用,可能造成内存浪费.
class Singleton {
//1. 构造器私有化, 外部不能new
private Singleton() {
}
//2.本类内部创建对象实例
private static Singleton instance;
static { // 在静态代码块中,创建单例对象
//静态代码块在类加载的时候执行,且值执行一次
instance = new Singleton();
}
//3. 提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
和上面1中的一样,只不过类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行了静态代码块中的代码,初始化类的实例.
和1.饿汉式(静态常量)一样
class Singleton {
private static Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,当使用到该方法时,才去创建 instance
//即懒汉式
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 起到了lazy loading的效果,但是只能在单线程下使用
- 如果在多线程下,一个线程进入了if(singleton == null)判断语句块,还来不及往下执行,另一个线程也通过了这个判断语句,这时会产生多个实例,所以在多线程环境下不能使用这种方式
-结论
在实际开发中,不要使用这种方式
// 懒汉式(线程安全,同步方法)
class Singleton {
private static Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
//即懒汉式
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 解决了线程安全的问题
- 效率太低了,每个线程在想获得该类实例的时候,执行getInstance()都要进行同步,而其实这个方法只要一次实例化代码就可以了,后面想要获得该类的实例,直接return就行了.方法进行同步效率太低
在实际开发中,不推荐使用这种方式
class Singleton {
private static Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,当使用到该方法时,才去创建 instance
//即懒汉式
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
其实也是线程不安全的,如果同时有几个线程进入了 if(instance == null) 代码块中,虽然同步了,但是也会生成不同的实例对象
结论
不推荐使用
class Singleton {
private static volatile Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题
//同时保证了效率, 推荐使用
public static synchronized Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- double-check概念是多线程开发中常用到的,
- 这样实例化代码值执行了一次,后面再次访问时,判断if(singleton == null),直接return实例化对象,也避免的反复进行同步方法
- 线程安全: 延迟加载,效率比较高
在实际开发中,推荐使用这种方法
// 静态内部类完成, 推荐使用
class Singleton {
private static volatile Singleton instance;
//构造器私有化
private Singleton() {}
//写一个静态内部类,该类中有一个静态属性 Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
//提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
public static synchronized Singleton getInstance() {
//调用该类的方法时加载类,做到了线程同步
return SingletonInstance.INSTANCE;
}
}
- 这种方式采用了类装载的机制来保证初始化实例时只有一个线程
- 静态内部类方式在Singleton类被加载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化
- 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程安全性,在类进行初始化时,别的线程是无法进入的.
- 优点: 避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
推荐使用
//使用枚举,可以实现单例, 推荐
enum Singleton {
INSTANCE; //属性
public void sayOK() {
System.out.println("ok~");
}
}
//使用
Singleton instance = Singleton.INSTANCE;
借助JDK1.5中添加的枚举来实现单例模式,不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
推荐使用
需要频繁进行创建和销毁的对象
创建对象时耗时过多或消耗资源过多(重量级对象),但是又需要经常使用到的对象,工具类对象,频繁访问数据库或文件的对象