Java单例,一篇就够了

2020-11-11  本文已影响0人  南风向北zhy
 本文从几种代码实现以及探究为啥写的方式叙述。话不多少,开始~

第一种:饿汉式

/**
 * 恶汉式
 */
public class Hangury {
    private Hangury(){}
    private  static  final  Hangury HANGURY=new Hangury();
    public  static  Hangury getInstance(){
        return  HANGURY;
    }
}
/**
 * 恶汉式
 */
public class Hangury {
    private Hangury(){}
    private  static    Hangury HANGURY;
    static {
        HANGURY =new Hangury();
    }
    public  static  Hangury getInstance(){
        return  HANGURY;
    }
}

恶汉分析:
优点:
1.线程安全
缺点:
1.类创建的时候对象就创建了,可能不会使用
2.可能会造成资源浪费,比如下面的代码

/**
 * 恶汉式
 */
public class Hangury {
    /***
     *  造成byte 数组占用内存
     */
    private    byte[] b1=new byte[1024*1024*1024];
    private Hangury(){}
    private  static    Hangury HANGURY;
    static {
        HANGURY =new Hangury();
    }
    public  static  Hangury getInstance(){
        return  HANGURY;
    }
}

第二种:懒汉式

/**
 * 懒汉式
 */
public class LazyMan {
    private  LazyMan(){}
    private   static   LazyMan lazyMan ;
    public  static  LazyMan getInstance(){
        if(lazyMan == null){
            lazyMan =new LazyMan();
        }
        return  lazyMan;
    }
}

避免了懒汉式的缺点,但是在多线程是不安全的,如下代码:

/**
 * 懒汉式
 */
public class LazyMan {
    private  LazyMan(){
        System.out.println(Thread.currentThread().getName()+"---run");
    }
    private   static   LazyMan lazyMan ;
    public  static  LazyMan getInstance(){
        if(lazyMan == null){
            lazyMan =new LazyMan();
        }
        return  lazyMan;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                public void run() {
                    LazyMan.getInstance();
                }
            }).start();
        }
    }
}

多次运行的结果可能不相同,线程创建多对象,导致创建的对象不是唯一

Thread-0----run
Thread-2----run
/**
 * 懒汉式
 */
public class LazyMan {
    private  LazyMan(){
        System.out.println(Thread.currentThread().getName()+"---run");
    }
    private   static   LazyMan lazyMan ;
    public  static  LazyMan getInstance(){
        if(lazyMan == null){  // ①
            synchronized (LazyMan.class){  //②
                if(lazyMan == null){  // ③
                    lazyMan =new LazyMan();
                }
            }
        }
        return  lazyMan;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                public void run() {
                    LazyMan.getInstance();
                }
            }).start();
        }
    }
}

市面上大多都是这样的,但是这个不是最优化的单例,因为创建对象涉及到原子性:Volatile.

/**
 * 懒汉式
 */
public class LazyMan {
    private  LazyMan(){
        System.out.println(Thread.currentThread().getName()+"---run");
    }
    private  volatile   static   LazyMan lazyMan ;
    public  static  LazyMan getInstance(){
        if(lazyMan == null){  // ①
            synchronized (LazyMan.class){  //②
                if(lazyMan == null){  // ③
                    lazyMan =new LazyMan();
                }
            }
        }
        return  lazyMan;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                public void run() {
                    LazyMan.getInstance();
                }
            }).start();
        }
    }
}

解释 :

正常的我们都认为 顺序是 1->2->3,但是有些时候 会是1->3->2,就会造成线程1进入锁创建对象的时候还没有创建完成,只是单纯的指向了内存,当第二个线程进入锁后直接返回的是没有创建成功的对象导致空指针

第三种 内部类

public class Holder {
    private Holder(){}
    public  static  Holder getInstance(){
        return InnerClass.holder;
    }
    private  static  class  InnerClass {
        private  static  final Holder holder =new Holder();
    }
}

第四种 枚举

public enum  SingleEmum {
    INSTANCE ;
    public  static  SingleEmum getInstance(){
        return  INSTANCE;
    }
}
class test{
    public static void main(String[] args) {
        final SingleEmum instance1 = SingleEmum.INSTANCE;
        final SingleEmum instance2 = SingleEmum.INSTANCE;
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

这些你都应该知道的,但是如何选择?

思考 :枚举虽然好,但是很少看到开源库或者大佬用到,这个是为啥 ?

上一篇下一篇

猜你喜欢

热点阅读