单例模式: 单例模式的意图是保证单例类在系统中有且仅有一个实例存在。
直接上代码看一下
/**
* 单例模式饿汉式
* static修饰,随类的加载而加载,只加载一次,天生就是线程安全的
* 缺点:没有lazy loading的效果,从而降低内存的使用率
* @author lz
*
*/
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton(){}//将构造器私有
public static Singleton getgetInstance(){
return instance;
}
}
//———————单例模式饿汉式end——————————————
/**
* 单例模式懒汉式
* 只适合单线程模式,只有当使用时才会创建
* @author lz
*
*/
class Singleton2 {
private static Singleton2 instance2=null;
private Singleton2(){}//将构造器私有
public static Singleton2 getgetInstance(){
if(instance2 == null){
instance2=new Singleton2();
}
return instance2;
}
}
//———————-单线程下的懒汉式end—————————————–
/**
* 单例模式懒汉式
* 多线程下,缺点:getInstance方法得到singleton实例的时候都有一个试图去获取同步锁的过程,很耗时的
* @author lz
*
*/
class Singleton3 {
private static Singleton3 instance3=null;
private Singleton3(){}//将构造器私有
public static synchronized Singleton3 getgetInstance(){
if(instance3 == null){
instance3=new Singleton3();
}
return instance3;
}
}
//——————加同步的懒汉式end———————————————
/**
* 单例模式懒汉式
* 多线程下,改良版,只有当instance为null时,需要获取同步锁,创建一次实例。当实例被创建,则无需试图加锁
* @author lz
*
*/
class Singleton4 {
private static Singleton4 instance4=null;
private Singleton4(){}//将构造器私有
public static Singleton4 getgetInstance(){
if(instance4 == null){
synchronized (Singleton4.class) {
if(instance4 == null){
instance4=new Singleton4();
}
}
}
return instance4;
}
}
/**
*
* 解决双重检查的缺点
* 防止反序列化和反射创建多个单列
*
* @author lz
*
*/
class Singleton8 implements Serializable{
private static final long serialVersionUID = 1L;
private static Singleton8 instance8=null;
private static int flgs=1;
private Singleton8(){
/**
* 防止反射攻击,只运行调用一次构造器,第二次抛异常
*/
if(flgs==1){
flgs++;
}else{
throw new RuntimeException("只能调用一次构造函数");
}
System.out.println("调用Singleton的私有构造器");
}
/**
* 用同步代码块的方式,在判断单例是否存在的if方法里使用同步代码块,在同步代码块中再次检查是否单例已经生成,
* 这也就是网上说的 双重检查加锁的方法
* @return
*/
public static synchronized Singleton8 getInstance(){
if(instance8==null){
synchronized (Singleton8.class) {
if(instance8==null){
instance8 = new Singleton8();
}
}
}
return instance8;
}
/**
*
* 防止反序列生成新的单例对象
* 序列化操作提供了一个很特别的钩子(hook)-类中具有一个私有的被实例化的方法readresolve(),
* 这个方法可以确保类的开发人员在序列化将会返回怎样的object上具有发言权
* 这样当JVM从内存中反序列化地"组装"一个新对象时,就会自动调用这个 readResolve方法来返回
* 我们指 定好的对象了, 单例规则也就得到了保证.
* 详解http://www.javalobby.org/java/forums/t17491.html
* @return
*/
private Object readResolve()throws ObjectStreamException{
return instance8;
}
}
//——————加双重检查机制的懒汉式end———————————————
/**
* 单例模式懒汉式
* 多线程下,在改良,上面的双重的if总是不提倡的。静态内部类的方式
* 当调用静态内部类时,将创建这个instance5 并且因为是static只创建一次
* classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗
* https://www.oschina.net/question/2273217_217864 这里是静态内部类加载顺序的问题
* @author lz
*
*/
class Singleton5 {
private Singleton5(){}//将构造器私有
//使用静态内部类
private static class Inner{
private static final Singleton5 instance5=new Singleton5();
}
public static Singleton5 getgetInstance(){
return Inner.instance5;
}
}
/**
* 静态内部类的缺点修补
* @author lz
*
*/
class Singleton9 implements Serializable{
private static final long serialVersionUID = 6397008420924619156L;
private static int flags=1;
private Singleton9(){//将构造器私有,防止反射
if(flags==1){
flags++;
}else{
throw new RuntimeException(“只能运行一次”);
}
System.out.println(“调用Singleton的私有构造器”);
}
//使用静态内部类
private static class Inner{
private static final Singleton9 instance9=new Singleton9();
}
public static Singleton9 getgetInstance(){
return Inner.instance9;
}
/**
*
* 防止反序列生成新的单例对象
* 序列化操作提供了一个很特别的钩子(hook)-类中具有一个私有的被实例化的方法readresolve(),
* 这个方法可以确保类的开发人员在序列化将会返回怎样的object上具有发言权
* 这样当JVM从内存中反序列化地"组装"一个新对象时,就会自动调用这个 readResolve方法来返回
* 我们指定好的对象了, 单例规则也就得到了保证.
* 详解http://www.javalobby.org/java/forums/t17491.html
* @return
*/
private Object readResolve()throws ObjectStreamException{
return Inner.instance9;
}
}
//————————–静态内部类的方式end————————————————
/**
* 枚举实现
* 上面的双重检查和静态内部类的形式缺点:
* 都需要额外的工作(Serializable、transient、readResolve())来实现序列化,*
* 否则每次反序列化一个序列 化的对象实例时都会创建一个新的实例。
* 可能会有人使用反射强行调用我们的私有构造器(如果要避免这种情况,可以修改构造器,
* 让它在创建第二个实例的时候抛异常)。
* 枚举除了线程安全和防止反射调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象
* http://www.cnblogs.com/yangzhilong/p/6148639.html 这里有介绍
* @author lz
*
*/
enum Singleton7{
instance;
public void whateverMethod(){}
}
/**
* 登记式单例,供参考 copy的
* http://blog.csdn.net/jason0539/article/details/23297037/
*
*/
最后,不管采取何种方案,请时刻牢记单例的四大要点:
线程安全
延迟加载
序列化与反序列化安全
反射