模式并不是只有java才有,它是一种思路。
为什么要用单例?
多个线程操作同一个对象就要用到单例。保证对象的唯一性
如何解决这个问题?
实例化的过程只实例化一次。多个线程开始到销毁到结束都用到同一个实例对象,提供返回实例对象的方法。
单例模式需要考虑的事项:线程的安全性、性能、懒加载(lazy:延迟加载)
单例的分类:
饿汉式:没有延时加载
线程安全性:在加载的时候已经被实例化(值类型直接放到工作空间,引用类型是将地址放到工作空间,引用类型的实例是放到主内存中),所以只有这一次,线程是安全的
性能:因为是静态的属性,虚拟机启动的时候就会实例化,所以用成员变量比较少的时候可以使用饿汉式单例,否则成员变量比较多会占用大量内存空间
public class HungerSingeton {
//加载的时候产生的实力对象
private static HungerSingeton Instance = new HungerSingeton();
private HungerSingeton(){
}
//返回的实例对象
public static HungerSingeton getInstance(){
return Instance;
}
//测试
public static void main(String args[]){
HungerSingeton hungerSingeton = getInstance();
System.out.println(hungerSingeton);
HungerSingeton hungerSingeton1 = getInstance();
System.out.println(hungerSingeton1);
}
}
测试结果:得到是相同的对象
多线程测试结果:结果也是一样的
懒汉式:延时加载
线程安全性:
性能:对于内存来说比较好,因为用到才创建,但是由于使用了Synchronized它退化到串行执行
public class HoonSingleton {
//私有静态
private static HoonSingleton instance = null;
//私有构造
private HoonSingleton(){
}
//返回的实例对象,什么使用
public synchronized static HoonSingleton getInstance(){
if(null == instance){
instance = new HoonSingleton();
}
return instance;
}
//改造后让锁的范围更小,只锁对象,而不锁方法,这样多线程访问这个方法的时候会都用有此方法的访问权,但是获取instance的时候会进行排队使用,但是此时还是不保证原子性
public static HoonSingleton getInstance1(){
if(null == instance){
synchronized(HoonSingleton.class){
instance = new HoonSingleton();
}
}
return instance;
}
//测试
public static void main(String args[]){
for (int i = 0; i < 10; i++) {
new Thread(()->{
HoonSingleton hoonSingleton = getInstance();
System.out.println(hoonSingleton);
},"getInstance").start();
}
}
}
下图出现的这种情况将不保证原子性,有可能创建两个实例。
HoonSingleton的getInstance方法分析
HoonSingleton的getInstance1方法的分析
DCLSingleton(Double-Check-Locking)式
安全性:保证了原子性
性能:与懒汉是一致,但是DCL模式会引起指令重排(happens-befor),空指针异常。
public class DCL {
//私有静态
private volatile static DCL instance = null;
//私有构造
private DCL(){
}
//返回的实例对象,什么使用
public static DCL getInstance(){
if(null == instance){
synchronized(DCL.class){
if(null == instance)
instance = new DCL();
}
}
return instance;
}
//测试
public static void main(String args[]){
for (int i = 0; i < 10; i++) {
new Thread(()->{
DCL dcl = getInstance();
System.out.println(dcl);
},"getInstance").start();
}
}
}
Volatile+DCL完美解决happens-befor规则带来的困扰
Holder模式
声明类的时候,成员变量中不声明实例变量,而放到内部静态类中
public class HolderDome {
private HolderDome(){
}
private static class Hoder{
private static HolderDome instance = new HolderDome();
}
//保证了原子性,懒加载,性能好,懒加载,内部类只有调用的时候才会创建
public static HolderDome getInstance(){
return Hoder.instance;
}
}
目前应用比较广泛的一种单例模式
枚举模式
public class EnumDomeSingleton {
//私有构造
private EnumDomeSingleton(){
}
//建立单例返回值
public static EnumDomeSingleton getInstance(){
return EnumHolder.INSTANCE.instance;
}
//内部枚举
private enum EnumHolder{
//定义EnumHolder枚举类型的常量
INSTANCE;
//定义需要单例的对象
private EnumDomeSingleton instance;
//定义枚举构造,在使用的时候才会创建实例
private EnumHolder(){
instance = new EnumDomeSingleton();
}
//创建返回的单例实例化
private EnumDomeSingleton getInstance(){
return instance;
}
}
//测试
public static void main(String args[]){
new Thread(()->{
for (int i = 0; i < 10; i++) {
EnumDomeSingleton enumDomeSingleton = EnumDomeSingleton.getInstance();
System.out.println(enumDomeSingleton);
}
}).start();
}
}
安全、性能好、逼格高、懒汉。
扩展:内部类特性,懒加载。