设计模式----单例模式

单例设计模式:

  1. 构造器私有化
  2. 提供静态私有的局部变量
  3. 提供静态公有的变量,用于获取单例对象。

调用效率非常频繁,建议采用饿汉;如果创建对象代价很高。则建议采用懒汉

饿汉式:

/**

 * 饿汉单例模式:

 * 特点:

 * 线程安全(加载类的时候,天然的线程安全的),调用效率高(不需要同步快)

 * @author jack

 *

 */

public class SingletonDemo01 {

    //类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的线程安全。

    private static SingletonDemo01 instance=new SingletonDemo01();//类初始化时,立即加载;

    private SingletonDemo01(){

       

    }

    public static SingletonDemo01 getInstance(){

        return instance;

    }

}

 

 

懒汉式:

/**

 *

 * @author jack

 *

 */

public class SingletonDemo02 {

    //类加载时,不进行初始化该对象,当真正使用时,才进行初始化。

    private static  SingletonDemo02 instance;

    private  SingletonDemo02(){//私有构造器

    }

    //方法同步,调用效率低

    public synchronized SingletonDemo02  getInstance(){

        if(null==instance){

            instance=new  SingletonDemo02();

        }

        return  instance;

    }

}

OK,加上synchronized关键字之后,getInstance方法就会锁上了。如果有两个线程(T1、T2)同时执行到这个方法时,会有其中一个线程T1获得同步锁,得以继续执行,而另一个线程T2则需要等待,当第T1执行完毕getInstance之后(完成了null判断、对象创建、获得返回值之后),T2线程才会执行执行。——所以这端代码也就避免了Version1中,可能出现因为多线程导致多个实例的情况。
但是,这种写法也有一个问题:给gitInstance方法加锁,虽然会避免了可能会出现的多个实例问题,但是会强制除T1之外的所有线程等待,实际上会对程序的执行效率造成负面影响。

/**

*

*懒汉模式比较好的方式

*/

// Version 4

public class Single4 {

    private static volatile Single4 instance;

    private Single4() {}

    public static Single4 getInstance() {

        if (instance == null) {

            synchronized (Single4.class) {

                if (instance == null) {

                    instance = new Single4();

                }

            }

        }

        return instance;

    }

}

 

 

静态内部类:

/**

 * 静态内部类的方式,创建单例模式

 * 优点:

 * 线程安全,调用效率高,延迟加载

 * @author jack

 *

 */

public class SingletonDemo04 {

    private static class SingleClassInstance{

        private  static final SingletonDemo04 instance=new SingletonDemo04();

    }

    private SingletonDemo04(){

    }

    public static SingletonDemo04  getInstance(){

        return SingleClassInstance.instance;

    }

}

 

 

枚举:

/**

 * 通过枚举方式实现单例

 * 注意:枚举本身就是单例,由jvm根本保证!避免通过反射和反序列化的漏洞

本身 是线程安全的,而且调用效率也高

 * 但是没有延迟加载,

 * @author jack

 *

 */

public enum SingletonDemo05 {

    /**

     * 定义一个枚举元素,他代表了一个 实例

     */

    INSTANCE;

    /**

     * 单例可以有自己的操作

     */

    public  void singletonOperation(){

        //功能处理

    }

    public static void main(String[] args) {

        SingletonDemo05 instance = SingletonDemo05.INSTANCE;

        SingletonDemo05 instance2 = SingletonDemo05.INSTANCE;

        System.out.println(instance==instance2);//true

    }

}

 

 

比较好的单例模式具备的优点:

  1. 线程安全
  2. 懒加载
  3. 调用效率要高 一些

通过反射破解单例模式:

通过反序列化破解单例模式:(注意反射和反序列化对于枚举是不起作用的)

/**

 * 测试懒汉式模式。(如何防止反射和反序列化破解枚举)

 *

 * @author jack

 *

 */

public class SingletonDemo06  implements Serializable {

    /**

     *

     */

    private static final long serialVersionUID = -3423017222120593309L;

    // 类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的线程安全。

    private static SingletonDemo06 instance = new SingletonDemo06();// 类初始化时,立即加载;

    private SingletonDemo06() {

        //防止反射

        if (instance != null) {

            throw new RuntimeException();

        }

    }

    public static SingletonDemo06 getInstance() {

        return instance;

    }

    //反序列化时,如果定义了readResolve则直接返回此方法指定的对象。而 不需要单独再创建对象。(单例模式,避免反序列化创建多个对象,则必须加入这个方法)

    private Object readResolve() throws  ObjectStreamException{

        return instance;

    }

}

测试:

/**

 * 测试反射和反序列化破解枚举

 * @author jack

 *

 */

public class Client02 {

    public static void main(String[] args) throws Exception {

        SingletonDemo06 instance = SingletonDemo06.getInstance();

        SingletonDemo06 instance2 = SingletonDemo06.getInstance();

        System.out.println(instance);

        System.out.println(instance2);

        System.out.println(instance == instance2);

        // 采用反射跳过

        // 获取类的注解码文件

        /*

         * Class<SingletonDemo06> clazz

         * =(Class<SingletonDemo06>)Class.forName("gof23.singleton.SingletonDemo06"); //获取构造器

         * Constructor<SingletonDemo06> constructor = clazz.getDeclaredConstructor(); //设置私有变量的可访问性

         * constructor.setAccessible(true); //创建对象 SingletonDemo06 instance3 =

         * constructor.newInstance(); SingletonDemo06 instance4 = constructor.newInstance();

         * System.out.println(instance3); System.out.println(instance4);

         * System.out.println(instance3==instance4);

         */

        // 测试反序列化

        FileOutputStream fileOutputStream = null;

        ObjectOutputStream objectOutputStream = null;

        try {

            fileOutputStream = new FileOutputStream("C://copy//a.txt");

            objectOutputStream = new ObjectOutputStream(fileOutputStream);

            objectOutputStream.writeObject(instance);

            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("C://copy//a.txt"));

            SingletonDemo06 instance6 = (SingletonDemo06) objectInputStream.readObject();

            System.out.println(instance6);

            System.out.println(instance6 == instance);

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            if (fileOutputStream != null) {

                fileOutputStream.close();

            }

            if (objectOutputStream != null) {

                objectOutputStream.close();

            }

        }

    }

}

 

单例多线程下的测试:

/**

 * 测试多线程环境下,创建单例模式的效率

 * @author jack

 *

 */

public class Client3 {

    public static void main(String[] args) throws Exception {

        int threadNum=10;

        final int circleNum=20000000;

        CountDownLatch countDownLatch = new CountDownLatch(threadNum);

        ExecutorService threadPool = Executors.newFixedThreadPool(threadNum);

        long beginTime=System.currentTimeMillis();

        for(int i=0;i<threadNum;i++){

            threadPool.execute(new Runnable() {

                @Override

                public void run() {

                      for(int j=0;j<circleNum;j++){

//                          SingletonDemo1 singletonDemo1= SingletonDemo1.getInstance();

//                          SingletonDemo2 singletonDemo2= SingletonDemo2.getInstance();

//                          SingletonDemo3 singletonDemo3= SingletonDemo3.getInstance();

//                          SingletonDemo4 singletonDemo4= SingletonDemo4.getInstance();

                          SingletonDemo5 singletonDemo5= SingletonDemo5.INSTANCE;

                      }

                      countDownLatch.countDown();

//                      System.out.println(countDownLatch.getCount());

                }

            });

        }

        countDownLatch.await();

        long endTime=System.currentTimeMillis();

        System.out.println("时间间隔:"+(endTime-beginTime));

        threadPool.shutdown();

    }

}

 

 

 

如有疑问,请发邮件:1176306419@qq.com


github:https://github.com/wangrui0/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值