唐僧喜欢小龙女

导航

彻底玩转单例模式

单例模式无论 new多少次,返回的实例对象在内存中的地址是一样的

1、饿汉式 在类加载的时候完成 类的初始化。

 

/**
 *
 *  一、饿汉式的意思
 *  类被加载完成后,对象已经被创建了。
 *
 *  二、饿汉式的缺点
 *  饿汉式可能会造成内存的浪费,如下面的例子,EHan 被加载后,data 也会被初始化。造成data 浪费内存。
 *
 *  
 */
public class EHan {

    //饿汉式单例 被jvm 加载了后 这个属性还没用呢,可能造成内存的浪费。
    private byte[] data = new byte[1024*1024];

    private EHan(){
        System.out.println("正在创建类");
    }


    private static final EHan HUNGRY = new EHan();

    public static EHan getHungry(){
        return HUNGRY;
    }


    //比如说这个方法被调用的时候EHan 已经被加载了,同时这类也创建了一个实例private static final EHan HUNGRY = new EHan();
    public static void ts(){
        System.out.println("ceshi");
    }



}

 

  

 

2、懒汉式

  

/**
 *  一、懒汉式的意思
 *      类被加载完成后,对象没有被创建呢,用的时候才创建对象
 *  二、懒汉式的缺点
 *      懒汉式,单线程下是没有问题的,多线程是有问题的。
 *      多个线程下来获取对象的时候不同的线程获取多个对象。内存里面会有多个对象存在。
 *
 */
public class LazyHan {

    private LazyHan(){
       System.out.println(Thread.currentThread().getName()+"正在创建对象");

    }

    private static LazyHan lazyHan;

    public static LazyHan getLazyHan(){
        if(lazyHan == null){
            lazyHan = new LazyHan();
        }

        return lazyHan;
    };

    //比如说这个方法被调用的时候EHan 已经被加载,但是 这个类的对象还是null(private static LazyHan lazyHan;).  构造方法也没有执行
    // 只有getLazyHan 这个方法被调用的时候才创建对象。
    public static void test1(){
        System.out.println("ceshi zhong ");
    }


    public static void main(String[] args) {
        int num = Runtime.getRuntime().availableProcessors();

        ThreadPoolExecutor excutors = new ThreadPoolExecutor(
                1,
                num,
                1000,  //超时等待的时间,就是345 中的窗口 这段时间没有人办理业务就要关闭,释放线程了。
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());//其中的一个拒绝策略,




        try{
            for (int i = 0; i < 94; i++) {
                excutors.execute(()->{
                   LazyHan.getLazyHan();

                });

            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            excutors.shutdown();
        }


    }

}

  

3、双重检查锁模式

 

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 *
 *   一、双重检查锁模式的意思是什么?
 *      两次判断对象是否为null加锁 这就是双重检查锁模式的意思
 *
 *   二、具体的实现
 *      判断 对象是不是空 是空的化 进入一个同步方法 方法里面 再判断了下 对象 是空就 new 对象 是推荐使用的。
 *      解决线程安全问题 和 实现懒加载。
 *
 */
public class SCS {

    private SCS(){
        System.out.println(Thread.currentThread().getName()+"正在创建对象");

    }
    // volatile 保证内存可见行、不保证原子性、禁止指令重排。这里加volatile 的原因主要是禁止指令重排防止极端情况下,返回的对象有问题。
    private volatile static SCS scs;

    public static final SCS getSCS(){
        if(scs == null){
            synchronized (SCS.class){
                if(scs == null){
                    scs = new SCS();   //这里不是一个 原子性的操作
                    /**
                     *  创建对象的内存操作
                     *  1、分配内存空间
                     *  2、执行构造方法
                     *  3、把这个对象指向这个空间
                     *
                     *  我们期望执行的时候是123 步骤来的,但是如果发生了指令重排导致执行步骤变成132,此时来了一个线程发现对象已经指向
                     *  了空间认为这个对象已经创建好了,但是没有执行构造方法,返回一个指向虚无的对象产生一些问题
                     *  为了防止出现这样的方式必须给 对象加一个 volatile
                     *
                     *
                     *
                     */
                }
            }
        }
        return scs;
    }


    public static void main(String[] args) {
        int num = Runtime.getRuntime().availableProcessors();

        ThreadPoolExecutor excutors = new ThreadPoolExecutor(
                1,
                num,
                1000,  //超时等待的时间,就是345 中的窗口 这段时间没有人办理业务就要关闭,释放线程了。
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());//其中的一个拒绝策略,




        try{
            for (int i = 0; i < 94; i++) {
                excutors.execute(()->{
                    SCS.getSCS();

                });

            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            excutors.shutdown();
        }


    }

 

  

 

4、静态内部类的方式

/**
 *
 *  内部类的方式创建单例模式
 *  这样的方式也不是安全的
 *
 */
public class OutClass {
    private OutClass(){};

     public static OutClass getOutClass(){
         return InnerClass.outClass;
     };

    public static class InnerClass{
       private static OutClass outClass = new OutClass();
    }
}

 

5、枚举类的方式获取对象

/**
 *
 *  枚举的方式创建单例模式
 *  
 *  除了枚举类创建单例别的方式都可以通过反射的方式破坏单例模式。
 *
 *
 */
public enum EnumSingle {

    INSTANCE;


    public static EnumSingle getInstance(){
        return INSTANCE;
    }


    public void test1(){
        System.out.println("测试通过枚举的方式创建类");
    }
}

  




posted on 2019-11-30 16:26  与时具进&不忘初心  阅读(154)  评论(0编辑  收藏  举报