一、单例模式概述
单例模式是一种常用的设计模式,也被称作单子模式。再用这个模式的时候,必须保证有且只有一个实例存在。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。避免一个通信端口同时被两个请求调用,用单例模式就可以避免这种不一致状态。
单例模式特点:
1、私有化类的构造函数,不能再让用户通过new的方法随意的产生对象
2、单例需要给类提供唯一一个单例对象
3、提供一个公共接口,返回唯一的单例对象
二、简单的单例模式的实现
1、饿汉模式:即先创建对象,等待调用
public class Danli1{
private static Danli1 danli1=new Danli1();
private Danli1(){ }
public static Danli1 getDanli1(){
return danli1;
}
}
2、懒汉模式:调用时再创建对象
public class Danli2{
private static Danli2 danli2;
private Danli2(){}
public static Danli2 getDanli2(){
if(danli2==null){
danli2=new Danli2();
}
return danli2;
}
}
由上面的代码可以看到,饿汉式的单例模式较为占用资源(因为还有没有用就创建好了),但是时间效率比较高;懒汉式的节省资源但是时间效率低。
注:上面两个单例模式在单线程的环境下时安全的,但是当处于多线程的环境下时,懒汉模式就会有线程安全的隐患。
对于饿汉,懒汉的多线程安全问题我们可以创建开启多个线程对单例模式的对象进行调用,然后用getHashCode的方法获取单例对象的哈希值,如果哈希值相同则线程安全,不同则不安全,在此处不再给出代码示例。最后得到的结果应该是饿汉式的哈希值皆相同,懒汉式的哈希值不同。
三、线程安全的单例模式
这里主要是针对懒汉式的单例模式的改进:
1、对公共方法加锁:(效率较差)
class Danli3{
private static Danli3 danli3;
private Danli3(){}
public static synchronized Danli3 getDanli3(){
if(danli3==null){
danli3=new Danli3();
}
return danli3;
}
}
2、同步代码块对获取对象加锁:(效率较差)
public class Danli4{
private static Danli4 danli4;
private Danli4(){}
public static Danli4 getDanli4(){
synchronized (Danli4.class){
if(danli4==null){
danli4=new Danli4();
}
}
return danli4;
}
}
3、静态内部类:(推荐)
public class Danli5{
private static class Hoder{
private static Danli5 danli5=new Danli5();
}
private Danli5(){ }
public static Danli5 getdanli5(){
return Hoder.danli5;
}
}
4、枚举实现线程安全,JVM安全的单例模式:(1.5+ 推荐)
枚举常可以和final定义的常量互换,用来定义有限值的列举,不能随意产生新的对象
枚举构造函数默认私有private。不能new对象
枚举通过反射产生对象已经在jdk中防止了,所以enum的JVM 安全
public enum Danli7{
INSTANCE;
private int id;
public int getId(){
return id;
}
public void setid(){
this.id=id;
}
}
===============================================================================================
tips.
其实除了上述所以说的线程安全的单例模式外,还可以用volatile和synchronized关键字实现一个双重检测同步延迟加载的单例模式。或者 用ThreadLocal去实现一个单例模式。在此不再给出。