定义:确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点,构造方法私有化
优点:减少了内存开销,可以避免对资源的多重占用,设置全局访问点,严格控制访问
缺点:没有接口,扩展比较难,要扩展的话只有修改代码
常见写法:饿汉单例,懒汉单例,注册式单例又叫容器式单例
饿汉单例(初始化创建)
private static final LazyStaticInnerClassSingleton aa=new LazyStaticInnerClassSingleton();
private LazyStaticInnerClassSingleton (){}
public static LazyStaticInnerClassSingleton getInstance(){
return aa;
}
优点:执行效率高,性能高,没有任何的锁
缺点:在某些情况下可能会造成内存浪费
解决办法懒汉式单例(外部类调用时才创建)
public class LazySimpleSingletion {
private static LazySimpleSingletion instance;
private LazySimpleSingletion(){};
public static LazySimpleSingletion getInstance(){
if (instance == null){
instance =new LazySimpleSingletion();
}
return instance;
}
}
优点:节省了内存
缺点:线程不安全(比如两个线程同时访问到,就会有两种结果,如果是同一个实例的话分
两种情况一种是正常顺序执行,一种是后者覆盖前者,如果不是同一个实例都是按顺序返回)
解决办法:加一个synchronized关键字,加了之后第一个线程没有执行完的,第二个线程会进入阻塞状态等待第一个线程执行完成。
public synchronized static LazySimpleSingletion getInstance(){
if (instance == null){
instance =new LazySimpleSingletion();
}
return instance;
}
缺点:性能低,等待时间太长,比如地铁站门口等待人数过多
解决办法:用方法内部类,和if判断是否有阻塞
public static LazySimpleSingletion getInstance(){
synchronized (LazySimpleSingletion.class){
if (instance == null){//这样其实还是阻塞并没有解决性能问题
instance =new LazySimpleSingletion();
}
}
return instance;
}
这个和上一次区别在于一个在地铁外面排队一个在地铁里面排队,虽然说在里面排队要好听一些但是问题还是在排队!
public static LazySimpleSingletion getInstance(){
if (instance == null){//放在这里就解决了这个问题,检查是否有阻塞
synchronized (LazySimpleSingletion.class){
if (instance == null){//这样其实还是阻塞并没有解决性能问题,检查是否要重新创建实例
instance =new LazySimpleSingletion();
}
}
}
return instance;
}
优点:性能提高
缺点:程序的可读性难度加大,不够优雅,这种方法会有指令排序的问题
指令重排序的问题是指(变量里面的值要指向new出来的对象时会有一个先后问题,一但出现问题就会造成线程紊乱)
解决办法:加volatile关键字比如:(private volatile static LazySimpleSingletion instance;
)
缺点:不够优雅
解决办法:
private LazyStaticInnerClassSingleton(){};
private static LazyStaticInnerClassSingleton getInstance(){
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE =new LazyStaticInnerClassSingleton();
}
优点:优雅,利用了java反射,性能高,内存不浪费
缺点:能够被反射破坏
解决办法:
//修改无参构造进行判断
private LazyStaticInnerClassSingleton(){
if (LazyHolder.INSTANCE!=null){
throw new RuntimeException("不允许非法访问");
}
};
缺点:虽然解决了反射破坏的可能但是不够优雅
解决办法:枚举式单例
public enum EnumSingleton {
INSTANCEOF;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCEOF;
}
}
缺点:会内存浪费,不适合大批量使用
优点:不会被破坏是因为枚举的底层不支持被反射拿到数据
解决办法:注册式单例——将所有实例都缓存到统一的容器中,使用标示进行获取
private static Map<String,Object> ioc=new ConcurrentHashMap<>();
public static Object getInstance(String className){
if(!ioc.containsKey(className)){
Object instance= null;
try {
//如果不存在就存到map里面
instance = Class.forName(className).newInstance();//newInstance是java反射时创建对象的方法
ioc.put(className,instance);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}else {
//如果存在就直接返回
return ioc.get(className);
}
}
优点:解决了大批量的问题
缺点:线程不安全
如果遇到序列化破坏单例怎么办?
解决办法:增加方法 private Object readResolve(){return INSTANCE}
//桥接模式。