Java初级 --单例模式
- 什么是单例模式 ?
单例模式(SingleTon Pattern)是最简单的一种设计模式。
单例模式的英文原话是 :
Ensure a class has only one instance,and provided a global point of access to it.
意思是 :确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
- 单例模式有哪些要求?
1、单例类只能有一个实例;
2、单例类必须自己创建自己的唯一实例;
3、单例类必须给所有其他对象提供这一实例。 - 单例模式的应用场景
单例模式的作用是确保一个类只有一个实例存在。
可以用在:
创建目录、数据库连接等需要单线程操作的场合,用于实现对系统资源的控制。
一个类可以定义无数个对象,但是只能有一个实例 - 在 Java 中实现单例模式通常有两种表现形式:
1、饿汉式单例类(类加载时,就进行对象实例化)
package test.java.hsdaihd;
public class SingleTon1 {
//类加载时,就进行对象实例化
private static SingleTon1 m_instance = new SingleTon1();
/**
* 构造函数私有,保证外界无法直接实例化
*/
private SingleTon1(){
}
/**
* 通过该方法获得实例对象(提供一个全局的访问点)
* @return
*/
public static SingleTon1 getInstance(){
return m_instance;
}
public static void main(String[] args) {
SingleTon1 singleTon1 = SingleTon1.getInstance();
SingleTon1 singleTon2 = SingleTon1.getInstance();
SingleTon1 singleTon3 = SingleTon1.getInstance();
System.out.println(singleTon1);
System.out.println(singleTon2);
System.out.println(singleTon3);
}
}
可见此时该类只出现了一个实例
2、懒汉式单例类(第一次引用类时,才进行对象实例化)
懒汉式单例类与饿汉式单例类相同的是,类的构造函数是私有的;不同的是,懒汉式单例类在加载时不会将自己实例化,而是在第一次被调用时将自己实例化。
SingleTon2 中 main 函数内容与SingleTon1同理,我们可以看到,很奇怪,为什么该类产生的不是一个实例,而是多个呢?
是因为在多线程环境下,线程 **A** 和线程 **B** 同时调用此方法,则执行if(m_instance == null)语句时都为真,那么线程A和线程B都会创建一个对象,在内存中就会有两个对象,这样就违反了单例模式。
那么如何来解决这一问题呢 ?就看接下来线程安全的单例模式。
- 线程安全的单例模式
package test.java.hsdaihd;
public class SingleTon2 {
private static Object lock = new Object();
private static SingleTon2 m_instance = null;
/**
* 构造方法私有,保证外界无法直接实例化
*/
private SingleTon2(){
}
/**
* 提供一个全局的访问点
* @return
*/
public static SingleTon2 getInstance(){
synchronized(lock) {
if (m_instance == null) {
m_instance = new SingleTon2();
}
}
return m_instance;
}
public static void main(String[] args) {
SingleTon2 singleTon1 = SingleTon2.getInstance();
SingleTon2 singleTon2 = SingleTon2.getInstance();
SingleTon2 singleTon3 = SingleTon2.getInstance();
System.out.println(singleTon1);
System.out.println(singleTon2);
System.out.println(singleTon3);
}
}
上述代码中,我们给if(m_instance == null) 外加了一个synchronized 的同步对象锁,这个时候该类就只有一个实例了。
亲爱的小伙伴们,这样做真的好吗?(高效 ?)我们来分析一下。
每一个线程,不管是否为 null ,都要加一次锁,而加锁、放锁都会消耗 CPU 资源,线程一多,会使资源浪费严重。
那么该如何解决呢?
先判断后加锁 ?确定吗 ?
明显违背了单例模式原则,那么我们该怎么办呢?不用怕,来~看~
这样做既防止了多线程资源浪费严重,又满足了一个类只有一个实例。这就是双重校验
- 内部类实现单例模式
我们选择静态内部类来实现单例模式。
package test.java.hsdaihd;
public class StaticSingleTon {
private StaticSingleTon(){
}
private static class SingleTon3 {
public static StaticSingleTon c = new StaticSingleTon();
}
public static StaticSingleTon getInstance(){
return SingleTon3.c;
}
public static void main(String[] args) {
StaticSingleTon staticSingleTon1 = StaticSingleTon.getInstance();
StaticSingleTon staticSingleTon2 = StaticSingleTon.getInstance();
StaticSingleTon staticSingleTon3 = StaticSingleTon.getInstance();
System.out.println(staticSingleTon1);
System.out.println(staticSingleTon2);
System.out.println(staticSingleTon3);
}
}
-
枚举实现单例模式
-
参考文献 《设计模式(Java版)》 韩静海编