单例模式
2016-07-07 21:18
357 查看
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。
单例对象通常作为程序中的存放配置信息的载体,因为它能保证其他对象读到一致的信息。例如在某个服务器程序中,该服务器的配置信息可能存放在数据库或文件中,这些配置数据由某个单例对象统一读取,服务进程中的其他对象如果要获取这些配置信息,只需访问该单例对象即可。这种方式极大地简化了在复杂环境 下,尤其是多线程环境下的配置管理,但是随着应用场景的不同,也可能带来一些同步问题。总之,选择单例模式就是为了避免不一致状态。
双重检测机制
静态初始化器是由static修饰的一对花括号“{}”括起来的语句组。它的作用和构造方法有待你相似,都是用来完成初始化工作的,但是静态初始化器与构造方法有以下几点根本不同。 a、构造方法是对每一个新创建的对象初始化,而静态方法是对类自身进行初始化。 b、构造方法是在new运算符创建新对象的时候由系统执行,而静态初始化器一般不能由程序调用,它是在所属类被加载入内存时由系统调用执行的。 c、用new运算符创建多少个新的对象,构造方法就被调用那个多少次,但是静态初始化器则是在被类加载入内存时只执行一次,与创建多少个对象无关。 2、如果有多个静态初始化器,则它们在类的初始化时会依次执行。 3、类是在第一次被使用的时候才被装载,而不是在程序启动时就装载程序中得所有可能用到的类。 4、静态初始化器的作用是对整个类完成初始化操作,包括给static成员变量赋初值,它在系统向内存加载时自动完成。
饿汉式单例模式1. //饿汉式单例类.在类初始化时,已经自行实例化 2. public class Singleton1 { 4. private Singleton1() {} 5. //已经自行实例化 6. private static final Singleton1 single = new Singleton1(); 7. //静态工厂方法 8. public static Singleton1 getInstance() { 9. return single; 10. } 11. }
饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
登记式单例实际上维护了一组单例类的实例。
线程安全:
饿汉式是线程安全的,可以直接用于多线程而不会出现问题,懒汉式就不行,它是线程不安全的,如果用于多线程可能会被实例化多次,失去单例的作用。
如果要把懒汉式用于多线程,有两种方式保证安全性,一种是在getInstance方法上加同步,另一种是在使用该单例方法前后加双锁。
资源加载:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,会占据一定的内存,相应的在调用时速度也会更快,而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次掉用时要初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
在多线程环境下,单例对象的同步问题主要体现在两个方面,单例对象的初始化和单例对象的属性更新。
a. 单例对象的属性(或成员变量)的获取,是通过单例对象的初始化实现的。也就是说,在单例对象初始化时,会从文件或数据库中读取最新的配置信息。
b. 其他对象不能直接改变单例对象的属性,单例对象属性的变化来源于配置文件或配置数据库数据的变化。
什么是类级内部类?
简单点说,类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。
类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。
在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:
由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
访问final字段时
在创建线程之前创建对象时
线程可以看见它将要处理的对象时
编写一个包含单个元素的枚举类型[极推荐]。代码如下:
public enum MaYun {
himself; //定义一个枚举的元素,就代表MaYun的一个实例
private String anotherField;
MaYun() { //MaYun诞生要做的事情
//这个方法也可以去掉。将构造时候需要做的事情放在instance赋值的时候:
/** himself = MaYun() { * //MaYun诞生要做的事情 * } **/ }
public void splitAlipay() { System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}
Call:MaYun.himself.splitAlipay();
Feature:从Java1.5开始支持;无偿提供序列化机制,绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候。
有几个原因关于为什么在Java中宁愿使用一个枚举量来实现单例模式:
1、 自由序列化;
2、 保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量);
3、 线程安全;
参考:http://callmegod.iteye.com/blog/1474441
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。
单例对象通常作为程序中的存放配置信息的载体,因为它能保证其他对象读到一致的信息。例如在某个服务器程序中,该服务器的配置信息可能存放在数据库或文件中,这些配置数据由某个单例对象统一读取,服务进程中的其他对象如果要获取这些配置信息,只需访问该单例对象即可。这种方式极大地简化了在复杂环境 下,尤其是多线程环境下的配置管理,但是随着应用场景的不同,也可能带来一些同步问题。总之,选择单例模式就是为了避免不一致状态。
双重检测机制
public class SingletonClass { private static volatile SingletonClass instance = null; public static SingletonClass getInstance() { if (instance == null) //先检查实例是否存在,如果不存在才进入下面的同步块
{ //同步块,线程安全的创建实例 synchronized (SingletonClass.class) { if (instance == null) {
//这里要进一步检查是否为null,因为两个线程都可能穿过第一个判断为空的条件,
当一个线程创建好,另一个线程苏醒过来,再次进入同步块,需要进一步进行判定! instance = new SingletonClass();
} } } return instance; } private SingletonClass() { } }
在某些情况下,JVM已经隐含的为您执行了同步,这些情况下就不用自己再来进行同步控制了。 * 这些情况包括: * (1)由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时 * (2)访问final字段时 * (3)在创建线程之前创建对象时 * (4)线程可以看见它将要处理的对象时
解决方案的思路 * 要想很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的 * 安全性。比如前面的饿汉式实现方式。但是这样一来,不是会浪费一定的空间吗?因为这种 * 实现方式,会在类装载的时候就初始化对象,不管你需不需要。 * 如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的 * 方式就是采用类级内部类,在这个类级内部类里面去创建对象实例。这样一来,只要不使用到这个类级内部类, * 那就不会创建对象实例,从而同步实现延迟加载和线程安全。
静态初始化器是由static修饰的一对花括号“{}”括起来的语句组。它的作用和构造方法有待你相似,都是用来完成初始化工作的,但是静态初始化器与构造方法有以下几点根本不同。 a、构造方法是对每一个新创建的对象初始化,而静态方法是对类自身进行初始化。 b、构造方法是在new运算符创建新对象的时候由系统执行,而静态初始化器一般不能由程序调用,它是在所属类被加载入内存时由系统调用执行的。 c、用new运算符创建多少个新的对象,构造方法就被调用那个多少次,但是静态初始化器则是在被类加载入内存时只执行一次,与创建多少个对象无关。 2、如果有多个静态初始化器,则它们在类的初始化时会依次执行。 3、类是在第一次被使用的时候才被装载,而不是在程序启动时就装载程序中得所有可能用到的类。 4、静态初始化器的作用是对整个类完成初始化操作,包括给static成员变量赋初值,它在系统向内存加载时自动完成。
public class SingletonClass { private static class SingletonClassInstance { private static SingletonClass instance = new SingletonClass(); } public static SingletonClass getInstance() { return SingletonClassInstance.instance; } private SingletonClass() { } }在这段代码中,因为SingletonClass没有static的属性,因此并不会被初始化。直到调用getInstance()的时候,会首先加载SingletonClassInstance类,这个类有一个static的SingletonClass实例,因此需要调用SingletonClass的构造方法,然后getInstance()将把这个内部类的instance返回给使用者。由于这个instance是static的,因此并不会构造多次类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例,没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
饿汉式单例模式1. //饿汉式单例类.在类初始化时,已经自行实例化 2. public class Singleton1 { 4. private Singleton1() {} 5. //已经自行实例化 6. private static final Singleton1 single = new Singleton1(); 7. //静态工厂方法 8. public static Singleton1 getInstance() { 9. return single; 10. } 11. }
饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
登记式单例实际上维护了一组单例类的实例。
线程安全:
饿汉式是线程安全的,可以直接用于多线程而不会出现问题,懒汉式就不行,它是线程不安全的,如果用于多线程可能会被实例化多次,失去单例的作用。
如果要把懒汉式用于多线程,有两种方式保证安全性,一种是在getInstance方法上加同步,另一种是在使用该单例方法前后加双锁。
资源加载:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,会占据一定的内存,相应的在调用时速度也会更快,而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次掉用时要初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
在多线程环境下,单例对象的同步问题主要体现在两个方面,单例对象的初始化和单例对象的属性更新。
a. 单例对象的属性(或成员变量)的获取,是通过单例对象的初始化实现的。也就是说,在单例对象初始化时,会从文件或数据库中读取最新的配置信息。
b. 其他对象不能直接改变单例对象的属性,单例对象属性的变化来源于配置文件或配置数据库数据的变化。
什么是类级内部类?
简单点说,类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。
类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。
在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:
由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
访问final字段时
在创建线程之前创建对象时
线程可以看见它将要处理的对象时
编写一个包含单个元素的枚举类型[极推荐]。代码如下:
public enum MaYun {
himself; //定义一个枚举的元素,就代表MaYun的一个实例
private String anotherField;
MaYun() { //MaYun诞生要做的事情
//这个方法也可以去掉。将构造时候需要做的事情放在instance赋值的时候:
/** himself = MaYun() { * //MaYun诞生要做的事情 * } **/ }
public void splitAlipay() { System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}
Call:MaYun.himself.splitAlipay();
Feature:从Java1.5开始支持;无偿提供序列化机制,绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候。
有几个原因关于为什么在Java中宁愿使用一个枚举量来实现单例模式:
1、 自由序列化;
2、 保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量);
3、 线程安全;
参考:http://callmegod.iteye.com/blog/1474441
相关文章推荐
- 2016.7.7
- Html学习笔记1
- Leetcode Longest Common Prefix
- 数据库连接池我的了解
- linux nohup
- ps top kill
- SQL Server 数据恢复
- win10系统使用clover时程序崩溃的解决
- a configuration error occurred during startup. place verify the preference field whth the prompt:TomcatJDK name:
- 字符集以及编码格式
- http、TCP/IP协议与socket之间的区别
- 2016sdau课程练习专题三 1009
- Win下Eclipse提交Hadoop程序出错:org.apache.hadoop.security.AccessControlException: Permission denied: user=A
- 字符串用switch-case
- python & go 语言完成最简单的web应用
- 2009年北航:字符串的查找删除
- 集训第二天
- PHP数据对象模型(PDO)
- Android下的Activity的四种启动模式
- session数据存入redis