设计模式之单例模式
Posted ProChick
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之单例模式相关的知识,希望对你有一定的参考价值。
一.简要概述
- 单例模式就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个获取该实例的静态方法。
- 单例模式确保了某一个类只有一个实例,而且会自行实例化并向整个系统提供这个实例,保证了自身唯一性。
二.模式结构
通常使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现
三.实现方式
1.饿汉式实现
线程安全、调用效率高、不能延时加载、有可能造成资源浪费
public class TestSingleton{
public static void main(String[] args){
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
// 实现一
class Singleton{
private final static Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return this.instance;
}
}
// 实现二
class Singleton{
private final static Singleton instance;
static{
instance = new Singleton();
}
private Singleton(){
}
public static Singleton getInstance(){
return this.instance;
}
}
2.懒汉式实现
单线程安全但在多线程情况下不安全、调用效率不高、可以延时加载,能够节约资源
public class TestSingleton{
public static void main(String[] args){
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
// 线程不安全
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
// 线程安全
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.双重检测锁实现
线程安全、调用效率高、可以延时加载,能够节约资源
public class TestSingleton{
public static void main(String[] args){
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
// 先判断instance是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
【注】:由于在JVM中可能出现指令重排的现象,也就是在多线程环境下,有可能一个对象被分配了地址空间但还没有进行初始化,所以就有可能创建多个对象。而volatile
关键字能够解决指令重排的问题,保证了变量值只要发生变化就会立即同步到主存。
4.静态内部类实现
线程安全、调用效率高、可以延时加载、能够节约资源
public class TestSingleton{
public static void main(String[] args){
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
public class Singleton {
private Singleton() {
}
private static class InnerSingleton {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return InnerSingleton.instance;
}
}
5.枚举类实现
线程安全、调用效率高、不能延时加载、有可能造成资源浪费
public class TestSingleton{
public static void main(String[] args){
Singleton s1 = Singleton.INSTANCE();
Singleton s2 = Singleton.INSTANCE();
System.out.println(s1 == s2);
}
}
public enum Singleton {
INSTANCE;
private String name;
public String getName {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
【注】:这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反射或反序列化手段破坏单例性。
四.注意事项
-
对于上述实现方式的前四种而言,我们其实可以通过反射或者反序列化的手段来破坏这种单例性。
通过反射进行破坏
public class TestSingleton{ public static void main(String[] args){ Constructor<?> constructor = Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); Singleton s1 = constructor.newInstance(); Singleton s2 = constructor.newInstance(); System.out.println(s1 == s2); } } class Singleton{ private final static Singleton instance = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return this.instance; } }
通过反序列化进行破坏
public class TestSingleton{ public static void main(String[] args){ Singleton s1 = Singleton.getInstance(); FileOutputStream fOutputStream = new FileOutputStream("序列化文件位置"); ObjectOutputStream outputStream = new ObjectOutputStream(fOutputStream); outputStream.writeObject(s1); FileInputStream fInputStream = new FileInputStream("序列化文件位置"); ObjectInputStream inputStream = new ObjectInputStream(fInputStream); Singleton s2= (Singleton)inputStream.readObject(); System.out.println(s1 == s2); } } class Singleton{ private final static Singleton instance = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return this.instance; } }
-
对于枚举实现的方式而言,其自身就是单例模式,而且由于JVM提供的安全保证,其不会受反射或者反序列化的影响。
五.优点好处
- 由于单例模式只生成一个实例,所以减少了系统性能开销,尤其是在生成大对象的时候
- 由于单例模式在系统中设置了全局访问点,所以优化了共享资源访问
六.如何选用
- 当所创建的对象所占用的资源比较少时,而且不看重延迟性,则选用饿汉式或枚举类
- 当所创建的对象所占用的资源比较多时,而且看重延迟性,则选用懒汉式或静态内部类或双重检测锁
七.应用场景
- 应用程序只需要一个该类的实例对象
- 客户调用某个实例只允许使用一个访问点
比如JDK源码中的Runtime类
以上是关于设计模式之单例模式的主要内容,如果未能解决你的问题,请参考以下文章