单例模式

单例

单例的方式

  • 饿汉式

    在实例使用之前,不管你用不用,先new,避免线程安全

  • 懒汉式

    默认加载的时候不实例化,在需要的时候才实例化,延迟加载

  • 注册登记式

    每使用一次,都向一个固定的容器中注册并且将使用过的对象进行缓存,下次取对象的时候,就直接从缓存中取值,保证每次获取都是同一个对象,IOC的单例,就是注册登记单例

  • 枚举式

    只要设置INSTANCE枚举构造函数

  • 序列化与反序列化

    重新写readResolve()

饿汉式

public class Hungry {
    private Hungry(){
​
    }
    //先静态,后动态
    //先属性,后方法
    //先上后下
    private static  Hungry hungry =new Hungry();
​
    public static Hungry getHungry() {
        System.out.println(System.currentTimeMillis()+","+hungry);
        return hungry;
    }
}

测试代码:

public static void main(String[] args) throws InterruptedException {
    int count=10;
    CountDownLatch countDownLatch=new CountDownLatch(count);
    for (int i=0;i<count; i++){
        Thread.sleep(1000);
        new Thread(()-> {
            try {
                System.out.println(System.currentTimeMillis()+","+ Hungry.getHungry());
               // System.out.println(countDownLatch.getCount());
​
                countDownLatch.countDown();
            } catch (Exception e) {
                e.printStackTrace();
            }
​
        }).start();
     }
    countDownLatch.await();
    System.out.println("执行完成");
​
  //  System.out.println( Hungry.getHungry());
}

结果:

1533773048357,com.zjm.single.Hungry@63d8e973
1533773049366,com.zjm.single.Hungry@63d8e973
1533773050370,com.zjm.single.Hungry@63d8e973
1533773051375,com.zjm.single.Hungry@63d8e973
1533773052394,com.zjm.single.Hungry@63d8e973
1533773053401,com.zjm.single.Hungry@63d8e973
1533773054415,com.zjm.single.Hungry@63d8e973
1533773055417,com.zjm.single.Hungry@63d8e973
1533773056418,com.zjm.single.Hungry@63d8e973
1533773057436,com.zjm.single.Hungry@63d8e973

从上面结果看出,饿汉式操作单例是线程安全的

懒汉式

代码:

package com.zjm.single;
​
public class LazyOne {
    private LazyOne(){}
    private static LazyOne lazyOne=null;
​
    public static LazyOne getInstance() {
        if(lazyOne==null){
            lazyOne=new LazyOne();
        }
        return lazyOne;
    }
}

上个测试代码改成LayOne.getInstance(),CountDownLach的个数成多一点例如:100

测试结果:

1533773305018,com.zjm.single.LazyOne@6f2b6701
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@74b47802
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@74b47802
1533773305018,com.zjm.single.LazyOne@5eca8d3a
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305018,com.zjm.single.LazyOne@315cb0c2
1533773305027,com.zjm.single.LazyOne@315cb0c2

看结果当前的实例不是同一个实例,懒汉式是线程不安全,要想是线程安全,可以加锁

public syncronized static LazyOne getInstance()

还有一种方式:

public class LazyThree {
    private  static boolean  initalized=false;
    //默认使用lazyThree的时候,先回初始化内部类
    private LazyThree(){
        synchronized (LazyThree.class){
            if (initalized==false){
                initalized=!initalized;
            }else{
                throw  new RuntimeException("单例已经初始化");
            }
        }
    }
    private static LazyThree lazyThree=null;
​
    private final static LazyThree getInstance() {
​
        return lazyHolder.LAZY_THREE;
    }
    private static  class lazyHolder{
        public static  final LazyThree LAZY_THREE=new LazyThree();
    }
}

上面这种方式首先,我现在加载内部类,这个内部类的实例是不会改变的,当我用到的时候再去初始化。

测试代码:

public class LazyThreeTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
         Class<?> clazz= LazyThree.class;
         Constructor cu=clazz.getDeclaredConstructor(null);
         cu.setAccessible(true);
         Object o= cu.newInstance();
         Object o1=cu.newInstance();
        //System.out.println(o==o1);
​
​
    }
}

利用反射机制去查看实例是否能被多次调用

结果:

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at com.zjm.single.test.LazyThreeTest.main(LazyThreeTest.java:14)
Caused by: java.lang.RuntimeException: 单例已经初始化
    at com.zjm.single.LazyThree.<init>(LazyThree.java:12)
    ... 5 more

上述结果不能实例化两次调用

注册登记式(spring Ioc容器会用到)

/**
 * spring 方式去注册单例
 */
public class BeanFactory {
    private BeanFactory(){}
    private static Map<String,Object> ioc=new ConcurrentHashMap<>() ;
    public  static Object getBean(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if(!ioc.containsKey(className)){
            Object obj=null;
            obj=Class.forName(className).newInstance();
            return obj;
        }else{
            return ioc.get(className);
        }
    }
}

测试代码:

​
public class BeanTest {
    public static void main(String[] args) {
        int count=100;
        CountDownLatch countDownLatch=new CountDownLatch(count);
        for (int i=0;i<count; i++){
            // Thread.sleep(2000);
            new Thread(()-> {
                try {
                    countDownLatch.await();
                    // System.out.println(countDownLatch.getCount());
​
                } catch (Exception e) {
                    e.printStackTrace();
                }
                try {
                    Object o=BeanFactory.getBean("com.zjm.single.Vo");
                    System.out.println(o);
​
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                }
​
​
            }).start();
            countDownLatch.countDown();
​
        }
       // System.out.println("执行完成");
    }
}

测试结果(部分数据):

com.zjm.single.Vo@749754de
com.zjm.single.Vo@623cb696
com.zjm.single.Vo@4d0d064b
com.zjm.single.Vo@55950ec
com.zjm.single.Vo@1b3f359d
com.zjm.single.Vo@7a978f7d
com.zjm.single.Vo@7a978f7d
com.zjm.single.Vo@7a978f7d
com.zjm.single.Vo@7a978f7d
com.zjm.single.Vo@7a978f7d
com.zjm.single.Vo@7a978f7d
com.zjm.single.Vo@623cb696
com.zjm.single.Vo@2ceac56c
com.zjm.single.Vo@7a978f7d
com.zjm.single.Vo@6ce865f6
com.zjm.single.Vo@39bfe578
com.zjm.single.Vo@63fb5fe6

线程不安全必须要加上 public syncronized static Object getBean()

序列化和反序列化

public class Seriable implements Serializable {
    //序列化就是说把内存中的状态通过转换成字节码的形式
    //从而转换一个IO流,写入到其他地方(可以是磁盘、网络IO)
    //内存中状态给永久保存下来了
​
    //反序列化
    //讲已经持久化的字节码内容,转换为IO流
    //通过IO流的读取,进而将读取的内容转换为Java对象
    //在转换过程中会重新创建对象new
​
    public  final static  Seriable INSTANCE =new Seriable();
    private Seriable(){
​
    }
    public static  Seriable getInstance(){
        return INSTANCE;
    }
    //这个是自带的方法序列化JVM虚拟机
    private Object readResolve(){
        return INSTANCE;
   }
}

为了保证数据保存的对象的时候不会再new

测试代码:

public class SeriableTest {
    public static void main(String[] args) {
        Seriable s1=null;
        Seriable s2=Seriable.getInstance();
        FileOutputStream fos=null;
        try{
            fos=new FileOutputStream("Seriable.obj");//保存序列化的数据,输出到磁盘中或者网络IO
            ObjectOutputStream oos=new ObjectOutputStream(fos);//将io流写入
            oos.writeObject(s2);//将输出流s2写入
            oos.flush();//刷新
            oos.close();//关闭
            FileInputStream fis=new FileInputStream("Seriable.obj");
            ObjectInputStream ois=new ObjectInputStream(fis);
            s1= (Seriable) ois.readObject();
            System.out.println(s1+"\n"+s2);
            System.out.println(s1==s2);
​
        }catch (Exception e){
​
        }
    }

输入对象和输出对象是否一致

测试结果:

com.zjm.single.seriable.Seriable@135fbaa4
com.zjm.single.seriable.Seriable@135fbaa4
true

上述是一个实例所以证明是单例

private Object readResolve(){
     return INSTANCE;
}

这个代码块去掉

结果为false

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值