章节目录:
一、单例模式介绍
1. 定义: 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得对象实例的方法。(静态方法)
2. 使用场景: 需要频繁创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:
重量级对象
),但又经常使用到的对象、工具类对象、频繁访问数据库或文件的对象(如:数据源、session工厂等。)3. 注意事项及说明:
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
- 想要实例化一个单例类时,必须要记住对应获取对象的方法,而不是使用
new
。
二、单例模式常见写法
方式一:*饿汉式(静态常量写法)
/* *
* 饿汉式(静态常量写法)
*/
public class Singleton {
/* *
* 1. 构造器私有,外部不能new
*/
private Singleton() {
}
/* *
* 2. 内部创建实例
*/
private static final Singleton INSTANCE = new Singleton();
/* *
* 3. 公有静态方法:返回实例对象
*/
public static Singleton getInstance() {
return INSTANCE;
}
}
class SingletonTest {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
// true
System.out.println("instance hashCode:" + instance.hashCode());
// instance hashCode:1239731077
System.out.println("instance1 hashCode:" + instance1.hashCode());
// instance1 hashCode:1239731077
}
}
- 方式说明:
优点: 在类装载的时候就完成实例化,写法简单,也避免了线程同步问题。
缺点: 类装载时完成实例化,没有达到
Lazy Loading
的效果。如果从始至终未使用过该实例,则会造成内存浪费。结论:单线程可用,但是可能造成内存浪费。
方式二:*饿汉式(静态代码块写法)
/* *
* 饿汉式(静态代码块写法)
*/
public class Singleton {
/* *
* 1. 构造器私有,外部不能new
*/
private Singleton() {
}
/* *
* 2. 定义一个静态实例变量,在静态代码块中创建实例对象
*/
private static Singleton instance;
static {
instance = new Singleton();
}
/* *
* 3. 公有静态方法:返回实例对象
*/
public static Singleton getInstance() {
return instance;
}
}
- 方式说明:
优点缺点: 方式类似,优缺点同上一致。
结论: 单线程可用,但是可能造成内存浪费。
方式三:懒汉式(线程不安全写法)
/* *
* 懒汉式(线程不安全写法)
*/
public class Singleton {
/* *
* 1. 定义一个静态实例变量
*/
private static Singleton instance;
/* *
* 2. 构造器私有,外部不能new
*/
private Singleton() {
}
public static Singleton getInstance() {
// 3. 实例为空则进行创建,并返回
if (null == instance) {
instance = new Singleton();
}
return instance;
}
}
- 方式说明:
优点: 起到了
Lazy Loading
的效果,但是只能在单线程下使用。缺点: 如果
多线程
下,一个线程进入到了判断语句块,未来得及创建实例。而另一个线程也进入了判断,则此时会创建多个实例。所有多线程环境下不可使用这种方式。结论: 在实际开发中,不要使用这种方式。
方式四:懒汉式(线程安全,同步写法)
/* *
* 懒汉式(线程安全,同步写法)
*/
public class Singleton {
/* *
* 1. 定义一个静态实例变量
*/
private static Singleton instance;
/* *
* 2. 构造器私有,外部不能new
*/
private Singleton() {
}
/* *
* 3. 增加synchronized关键字,使其同步,避免线程不安全问题
*/
public static synchronized Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance;
}
}
- 方式说明:
优点: 解决了线程不安全问题。
缺点: 效率太低,每次想要获得实例的时候,都需要进行同步。而其实只用执行一次实例化代码就够了,后面想获得该实例,直接return就行了。
结论: 在实际开发中,不推荐使用这种方式。
方式五:*双重检查机制
/* *
* 双重检查机制写法
*/
public class Singleton {
/* *
* 1. 定义一个用volatile修饰的静态同步实例变量
*/
private static volatile Singleton instance;
/* *
* 2. 构造器私有,外部不能new
*/
private Singleton() {
}
/* *
* 3. 加入双重判断,并进行同步。解决线程不安全及效率问题
*/
public static Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
- 逻辑示意图:
- 方式说明:
特点:
Double-Check
概念是多线程
常用的。进行两次(null == instance)
检查,保证了线程安全。这样
实例化
代码只用执行一次,后面再次访问,直接返回实例对象,避免反复进行方法同步。线程安全,延迟加载,效率较高。
结论: 在实际开发中,推荐使用这种模式。
方式六:*静态内部类
/* *
* 静态内部类
*/
public class Singleton {
/* *
* 1. 构造器私有,外部不能new
*/
private Singleton() {
}
/* *
* 2. 定义一个静态内部类,该类有静态属性Singleton
*/
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
/* *
* 3. 提供公有方法,调用时返回实例
*/
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
- 方式说明:
特点:
采用了
类装载机制
保证了初始化实例只有一个线程。只有在调用
getInstance()
时,才会装载SingletonInstance
类,从而完成new Singleton()
操作。类的静态属性只会在第一次加载类的时候初始化,JVM帮助我们提供了线程的安全性,在类进行初始化的时候,别的线程是无法进入的。
避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。
结论:推荐使用。
方式七:*枚举
/* *
* 使用枚举方式实现单例
*/
public class SingletonTest {
enum Singleton {
// 属性
INSTANCE;
/* *
* 枚举中可以定义方法,并通过属性实例调用
*/
public void sayOk() {
System.out.println("ok");
}
}
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance1 = Singleton.INSTANCE;
instance.sayOk();
instance1.sayOk();
// ok
System.out.println(instance == instance1);
// true
System.out.println(instance.hashCode());
// 1239731077
System.out.println(instance1.hashCode());
// 1239731077
}
}
- 方式说明:
特点:
借助
JDK1.5
中添加的枚举来实现单例模式。不仅能避免多线程问题,而且还能防止
反序列化
重新创建新的对象。这种方式是《Effective Java》作者(美国)Joshua Bloch提倡的方式。
结论:推荐使用。
三、该模式在JDK中的应用
java.lang
包下的Runtime
类 :采用了饿汉式的方式实现单例。
四、结束语
“-------怕什么真理无穷,进一寸有一寸的欢喜。”
微信公众号搜索:饺子泡牛奶。