并发编程基础06-单例模式

1.饿汉式单例

示例:

/**
 * 单例模式1:饿汉式
 * @author bruceliu
 * @create 2019-02-28 11:27
 */
public class MySingeleton {


    private static MySingeleton instance=new MySingeleton();

    public MySingeleton(){

    }

    public static MySingeleton getInstance(){
        return instance;
    }
}

测试代码:

/**
* 测试饿汉式
*
* @author bruceliu
* @create 2019-02-28 11:32
*/
public class TestMySingeleton extends Thread {

   @Override
   public void run() {
       System.out.println("对象地址:" + MySingeleton.getInstance() + "---->hashcode:" + MySingeleton.getInstance().hashCode());
   }

   public static void main(String[] args) {

       TestMySingeleton[] threads = new TestMySingeleton[10];

       for (int i = 0; i < threads.length; i++) {
           threads[i] = new TestMySingeleton();
       }

       for (int i = 0; i < threads.length; i++) {
           threads[i].start();
       }

   }
}

运行结果:

对象地址:com.bruceliu.demo3.MySingeleton@60c8c409---->hashcode:1623770121
对象地址:com.bruceliu.demo3.MySingeleton@60c8c409---->hashcode:1623770121
对象地址:com.bruceliu.demo3.MySingeleton@60c8c409---->hashcode:1623770121
对象地址:com.bruceliu.demo3.MySingeleton@60c8c409---->hashcode:1623770121
对象地址:com.bruceliu.demo3.MySingeleton@60c8c409---->hashcode:1623770121
对象地址:com.bruceliu.demo3.MySingeleton@60c8c409---->hashcode:1623770121
对象地址:com.bruceliu.demo3.MySingeleton@60c8c409---->hashcode:1623770121
对象地址:com.bruceliu.demo3.MySingeleton@60c8c409---->hashcode:1623770121
对象地址:com.bruceliu.demo3.MySingeleton@60c8c409---->hashcode:1623770121
对象地址:com.bruceliu.demo3.MySingeleton@60c8c409---->hashcode:1623770121

从运行结果可以看出实例变量额hashCode值一致,这说明对象是同一个。

2.懒汉式单例

示例:

/**
 * 单例设计模式:懒汉式
 * @author bruceliu
 * @create 2019-02-28 11:38
 */
public class MySingleton {

    private static MySingeleton instace=null;

    private MySingleton(){

    }

    public static  MySingeleton getInstace(){
        if(instace==null){
            try {
                //创建实例之前可能会有一些准备性的耗时工作
                Thread.sleep(3000);
                instace=new MySingeleton();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return instace;
    }
}

测试代码:

/**
 * 测试懒汉式单例设计模式
 * @author bruceliu
 * @create 2019-02-28 11:43
 */
public class TestMySingeleton extends Thread {

    @Override
    public void run() {
        System.out.println("对象地址:" + MySingleton.getInstace() + "---->hashcode:" + MySingleton.getInstace().hashCode());
    }

    public static void main(String[] args) {
        TestMySingeleton[] threads=new TestMySingeleton[10];

        for (int i = 0; i <threads.length ; i++) {
            threads[i]=new TestMySingeleton();
        }

        for (int i = 0; i <threads.length ; i++) {
            threads[i].start();
        }
    }

}

运行结果:

对象地址:com.bruceliu.demo3.MySingeleton@1fee0706---->hashcode:1905381423
对象地址:com.bruceliu.demo3.MySingeleton@5f73095---->hashcode:100085909
对象地址:com.bruceliu.demo3.MySingeleton@43008f8f---->hashcode:1905381423
对象地址:com.bruceliu.demo3.MySingeleton@692db0ef---->hashcode:100085909
对象地址:com.bruceliu.demo3.MySingeleton@7e92ee0---->hashcode:1905381423
对象地址:com.bruceliu.demo3.MySingeleton@48e0ce1c---->hashcode:1176975728
对象地址:com.bruceliu.demo3.MySingeleton@46273970---->hashcode:1764602095
对象地址:com.bruceliu.demo3.MySingeleton@7397005b---->hashcode:1764602095
对象地址:com.bruceliu.demo3.MySingeleton@4e704f06---->hashcode:1315983110
对象地址:com.bruceliu.demo3.MySingeleton@7191d02f---->hashcode:1905381423

从这里执行结果可以看出,单例的线程安全性并没有得到保证.

3.懒汉式单例(线程安全-同步方法)

示例:

/**
 * 线程安全的懒汉式单例-方法中声明synchronized关键字
 *
 * @author bruceliu
 * @create 2019-02-28 21:03
 */
public class MySingleton {

    private static MySingleton instance = null;

    private MySingleton() {

    }

    public synchronized static MySingleton getInstance(){
        try {
            if(instance!=null){

            }else{
                //创建实例之前可能会有一些准备性的耗时工作
                Thread.sleep(3000);
                instance=new  MySingleton();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return instance;
    }
}

测试代码:

/**
 * 测试懒汉式单例设计模式-方法中声明synchronized关键字
 * @author bruceliu
 * @create 2019-02-28 11:43
 */
public class TestMySingeleton extends Thread {

    @Override
    public void run() {
        System.out.println("对象地址:" + MySingleton.getInstance() + "---->hashcode:" + MySingleton.getInstance().hashCode());
    }

    public static void main(String[] args) {
        TestMySingeleton[] threads=new TestMySingeleton[10];

        for (int i = 0; i <threads.length ; i++) {
            threads[i]=new TestMySingeleton();
        }

        for (int i = 0; i <threads.length ; i++) {
            threads[i].start();
        }
    }

}

运行结果:

对象地址:com.bruceliu.demo5.MySingleton@524a6f4d---->hashcode:1380609869
对象地址:com.bruceliu.demo5.MySingleton@524a6f4d---->hashcode:1380609869
对象地址:com.bruceliu.demo5.MySingleton@524a6f4d---->hashcode:1380609869
对象地址:com.bruceliu.demo5.MySingleton@524a6f4d---->hashcode:1380609869
对象地址:com.bruceliu.demo5.MySingleton@524a6f4d---->hashcode:1380609869
对象地址:com.bruceliu.demo5.MySingleton@524a6f4d---->hashcode:1380609869
对象地址:com.bruceliu.demo5.MySingleton@524a6f4d---->hashcode:1380609869
对象地址:com.bruceliu.demo5.MySingleton@524a6f4d---->hashcode:1380609869
对象地址:com.bruceliu.demo5.MySingleton@524a6f4d---->hashcode:1380609869
对象地址:com.bruceliu.demo5.MySingleton@524a6f4d---->hashcode:1380609869

从执行结果上来看,问题已经解决了,但是这种实现方式的运行效率会很低。同步方法效率低,那我们考虑使用同步代码块来实现.

6.懒汉式单例(线程安全-同步代码块)

示例:

/**
 * 线程安全的懒汉式单例-同步代码块实现
 *
 * @author bruceliu
 * @create 2019-02-28 21:03
 */
public class MySingleton {

    private static MySingleton instance = null;

    private MySingleton() {

    }

    public static MySingleton getInstance() {
        try {
            synchronized (MySingleton.class) {
                if (instance != null) {

                } else {
                    //创建实例之前可能会有一些准备性的耗时工作
                    Thread.sleep(3000);
                    instance = new MySingleton();
                }
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return instance;
    }
}

测试代码:

/**
 * 测试懒汉式单例设计模式-同步代码块实现
 * @author bruceliu
 * @create 2019-02-28 11:43
 */
public class TestMySingeleton extends Thread {

    @Override
    public void run() {
        System.out.println("对象地址:" + MySingleton.getInstance() + "---->hashcode:" + MySingleton.getInstance().hashCode());
    }

    public static void main(String[] args) {
        TestMySingeleton[] threads=new TestMySingeleton[10];

        for (int i = 0; i <threads.length ; i++) {
            threads[i]=new TestMySingeleton();
        }

        for (int i = 0; i <threads.length ; i++) {
            threads[i].start();
        }
    }
}

运行结果:

对象地址:com.bruceliu.demo6.MySingleton@338637c5---->hashcode:864434117
对象地址:com.bruceliu.demo6.MySingleton@338637c5---->hashcode:864434117
对象地址:com.bruceliu.demo6.MySingleton@338637c5---->hashcode:864434117
对象地址:com.bruceliu.demo6.MySingleton@338637c5---->hashcode:864434117
对象地址:com.bruceliu.demo6.MySingleton@338637c5---->hashcode:864434117
对象地址:com.bruceliu.demo6.MySingleton@338637c5---->hashcode:864434117
对象地址:com.bruceliu.demo6.MySingleton@338637c5---->hashcode:864434117
对象地址:com.bruceliu.demo6.MySingleton@338637c5---->hashcode:864434117
对象地址:com.bruceliu.demo6.MySingleton@338637c5---->hashcode:864434117
对象地址:com.bruceliu.demo6.MySingleton@338637c5---->hashcode:864434117

这里的实现能够保证多线程并发下的线程安全性,但是这样的实现将全部的代码都被锁上了,同样的效率很低下。

7.懒汉式单例(线程安全-同步代码块-针对重要的代码进行同步)

示例:

/**
 * 线程安全的懒汉式单例-同步代码块实现(缩小的同步代码块的粒度,但是这样还是可能存在线程不安全性)
 *
 * @author bruceliu
 * @create 2019-02-28 21:12
 */
public class MySingleton {

    private static MySingleton instance = null;

    private MySingleton() {
    }

    public static MySingleton getInstance() {
        try {
            if (instance != null) {//懒汉式

            } else {
                //创建实例之前可能会有一些准备性的耗时工作
                Thread.sleep(300);
                synchronized (MySingleton.class) {
                    instance = new MySingleton();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return instance;
    }
}

测试:

/**
 * 测试懒汉式单例设计模式-同步代码块实现
 * @author bruceliu
 * @create 2019-02-28 11:43
 */
public class TestMySingeleton extends Thread {

    @Override
    public void run() {
        System.out.println("对象地址:" + MySingleton.getInstance() + "---->hashcode:" + MySingleton.getInstance().hashCode());
    }

    public static void main(String[] args) {
        TestMySingeleton[] threads=new TestMySingeleton[10];

        for (int i = 0; i <threads.length ; i++) {
            threads[i]=new TestMySingeleton();
        }

        for (int i = 0; i <threads.length ; i++) {
            threads[i].start();
        }
    }

}

运行结果:

对象地址:com.bruceliu.demo7.MySingleton@14c448fe---->hashcode:1348345633
对象地址:com.bruceliu.demo7.MySingleton@1b094e2b---->hashcode:1348345633
对象地址:com.bruceliu.demo7.MySingleton@2a0170e1---->hashcode:1348345633
对象地址:com.bruceliu.demo7.MySingleton@79af4c1c---->hashcode:1348345633
对象地址:com.bruceliu.demo7.MySingleton@35655ed5---->hashcode:1348345633
对象地址:com.bruceliu.demo7.MySingleton@37e684d---->hashcode:1348345633
对象地址:com.bruceliu.demo7.MySingleton@505e1f21---->hashcode:1348345633
对象地址:com.bruceliu.demo7.MySingleton@2a75ae17---->hashcode:1348345633
对象地址:com.bruceliu.demo7.MySingleton@51d1628b---->hashcode:1348345633
对象地址:com.bruceliu.demo7.MySingleton@543fe730---->hashcode:1348345633

从运行结果来看,这样的方法进行代码块同步,代码的运行效率是能够得到提升,但是却没能保住线程的安全性。看来还得进一步考虑如何解决此问题。

8.双检查锁机制

示例:

/**
 * Double Check Locking 双检查锁机制
 * @author bruceliu
 * @create 2019-02-28 21:14
 */
public class MySingleton {

    //使用volatile关键字保其可见性
    volatile  private static MySingleton instance= null;

    private MySingleton(){

    }

    public static MySingleton getInstance(){
        try {
            if(instance!=null){

            }else{
                //耗时操作
                Thread.sleep(3000);
                synchronized (MySingleton.class){
                    if(instance==null){
                        //二次检查
                        instance=new MySingleton();
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return  instance;
    }
}

测试代码:

/**
 * 测试懒汉式单例设计模式-Double Check Locking 双检查锁机制
 * @author bruceliu
 * @create 2019-02-28 11:43
 */
public class TestMySingeleton extends Thread {

    @Override
    public void run() {
        System.out.println("对象地址:" + MySingleton.getInstance() + "---->hashcode:" + MySingleton.getInstance().hashCode());
    }

    public static void main(String[] args) {
        TestMySingeleton[] threads=new TestMySingeleton[10];

        for (int i = 0; i <threads.length ; i++) {
            threads[i]=new TestMySingeleton();
        }

        for (int i = 0; i <threads.length ; i++) {
            threads[i].start();
        }
    }
}

从运行结果来看,该中方法保证了多线程并发下的线程安全性。
这里在声明变量时使用了volatile关键字来保证其线程间的可见性;在同步代码块中使用二次检查,以保证其不被重复实例化。集合其二者,这种实现方式既保证了其高效性,也保证了其线程安全性。

9.单例模式(静态内部类)

示例:

/**
 * 使用静态内置类实现单例模式
 * @author bruceliu
 * @create 2019-02-28 21:20
 */
public class MySingleton {

    //内部类
    public static class MySingletonHandler {
        private static MySingleton instance = new MySingleton();
    }

    private MySingleton() {

    }

    public static MySingleton getInstance() {
        return MySingletonHandler.instance;
    }

}

测试代码:

/**
 * 使用静态内置类实现单例模式 测试
 * @author bruceliu
 * @create 2019-02-28 11:43
 */
public class TestMySingeleton extends Thread {

    @Override
    public void run() {
        System.out.println("对象地址:" + MySingleton.getInstance() + "---->hashcode:" + MySingleton.getInstance().hashCode());
    }

    public static void main(String[] args) {
        TestMySingeleton[] threads=new TestMySingeleton[10];

        for (int i = 0; i <threads.length ; i++) {
            threads[i]=new TestMySingeleton();
        }

        for (int i = 0; i <threads.length ; i++) {
            threads[i].start();
        }
    }

}

运行结果:

对象地址:com.bruceliu.demo9.MySingleton@49fb8056---->hashcode:1241219158
对象地址:com.bruceliu.demo9.MySingleton@49fb8056---->hashcode:1241219158
对象地址:com.bruceliu.demo9.MySingleton@49fb8056---->hashcode:1241219158
对象地址:com.bruceliu.demo9.MySingleton@49fb8056---->hashcode:1241219158
对象地址:com.bruceliu.demo9.MySingleton@49fb8056---->hashcode:1241219158
对象地址:com.bruceliu.demo9.MySingleton@49fb8056---->hashcode:1241219158
对象地址:com.bruceliu.demo9.MySingleton@49fb8056---->hashcode:1241219158
对象地址:com.bruceliu.demo9.MySingleton@49fb8056---->hashcode:1241219158
对象地址:com.bruceliu.demo9.MySingleton@49fb8056---->hashcode:1241219158
对象地址:com.bruceliu.demo9.MySingleton@49fb8056---->hashcode:1241219158

静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的
示例:

/**
 * 静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的
 * @author bruceliu
 * @create 2019-02-28 21:25
 */
public class MySingleton implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class MySingletonHandler {
        private static MySingleton instance = new MySingleton();
    }

    private MySingleton(){}


    public static MySingleton getInstance(){
        return  MySingletonHandler.instance;
    }

}

测试代码:

/**
* 测试序列化与反序列化的单例模式实现
* @author bruceliu
* @create 2019-02-28 21:29
*/
public class SaveAndReadForSingleton {

   public static void main(String[] args) {

       MySingleton singleton = MySingleton.getInstance();

       File file = new File("MySingleton.txt");

       try {
           FileOutputStream fos = new FileOutputStream(file);
           ObjectOutputStream oos = new ObjectOutputStream(fos);
           oos.writeObject(singleton);
           fos.close();
           oos.close();
           System.out.println("--------->"+singleton.hashCode());
       } catch (FileNotFoundException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       }

       try {
           FileInputStream fis = new FileInputStream(file);
           ObjectInputStream ois = new ObjectInputStream(fis);
           MySingleton rSingleton = (MySingleton) ois.readObject();
           fis.close();
           ois.close();
           System.out.println("--------->"+rSingleton.hashCode());

       } catch (FileNotFoundException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       }

   }

}

运行结果:

--------->2133927002
--------->1836019240

解决办法就是在反序列化的过程中使用readResolve()方法,单例实现的代码如下:

/**
 * 静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的
 * @author bruceliu
 * @create 2019-02-28 21:25
 */
public class MySingleton implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class MySingletonHandler {
        private static MySingleton instance = new MySingleton();
    }

    private MySingleton(){}


    public static MySingleton getInstance(){
        return  MySingletonHandler.instance;
    }

    //该方法在反序列化时会被调用,该方法不是接口定义的方法
    protected Object readResolve() throws ObjectStreamException {
        System.out.println("调用了readResolve方法!");
        return MySingletonHandler.instance;
    }
}

运行结果:

--------->2133927002
调用了readResolve方法!
--------->2133927002

10.单例模式(静态代码块)

示例:

/**
 * 使用static代码块实现单例
 * @author bruceliu
 * @create 2019-02-28 21:31
 */
public class MySingleton {

    private static MySingleton instance=null;

    private MySingleton(){

    }

    static {
        instance=new MySingleton();
    }

    public static MySingleton getInstance(){
        return instance;
    }

}

测试代码:

/**
 * 使用静态内置类实现单例模式 测试
 * @author bruceliu
 * @create 2019-02-28 11:43
 */
public class TestMySingeleton extends Thread {

    @Override
    public void run() {
        System.out.println("对象地址:" + MySingleton.getInstance() + "---->hashcode:" + MySingleton.getInstance().hashCode());
    }

    public static void main(String[] args) {
        TestMySingeleton[] threads=new TestMySingeleton[10];

        for (int i = 0; i <threads.length ; i++) {
            threads[i]=new TestMySingeleton();
        }

        for (int i = 0; i <threads.length ; i++) {
            threads[i].start();
        }
    }

}

运行结果:

对象地址:com.bruceliu.demo11.MySingleton@586071af---->hashcode:1482715567
对象地址:com.bruceliu.demo11.MySingleton@586071af---->hashcode:1482715567
对象地址:com.bruceliu.demo11.MySingleton@586071af---->hashcode:1482715567
对象地址:com.bruceliu.demo11.MySingleton@586071af---->hashcode:1482715567
对象地址:com.bruceliu.demo11.MySingleton@586071af---->hashcode:1482715567
对象地址:com.bruceliu.demo11.MySingleton@586071af---->hashcode:1482715567
对象地址:com.bruceliu.demo11.MySingleton@586071af---->hashcode:1482715567
对象地址:com.bruceliu.demo11.MySingleton@586071af---->hashcode:1482715567
对象地址:com.bruceliu.demo11.MySingleton@586071af---->hashcode:1482715567
对象地址:com.bruceliu.demo11.MySingleton@586071af---->hashcode:1482715567

11.单例模式(枚举)

枚举enum和静态代码块的特性相似,在使用枚举时,构造方法会被自动调用,利用这一特性也可以实现单例
示例:

/**
 * 使用枚举数据类型实现单例模式
 *
 * @author bruceliu
 * @create 2019-02-28 21:36
 */
public enum EnumFactory {

    singletonFactory;

    private MySingleton instance;

    private EnumFactory() {
        //枚举类的构造方法在类加载是被实例化
        instance = new MySingleton();
    }

    public MySingleton getInstance() {
        return instance;
    }

}

class MySingleton {

    public MySingleton() {

    }
}

测试代码:

/**
 * 测试使用枚举数据类型实现单例模式
 * @author bruceliu
 * @create 2019-02-28 21:41
 */
public class TestMySingleton extends Thread {

    @Override
    public void run() {
        System.out.println(EnumFactory.singletonFactory.getInstance().hashCode());
    }

    public static void main(String[] args) {

        TestMySingleton[] mts = new TestMySingleton[10];

        for (int i = 0; i < mts.length; i++) {
            mts[i] = new TestMySingleton();
        }

        for (int j = 0; j < mts.length; j++) {
            mts[j].start();
        }

    }
}

运行结果:

1416179411
1416179411
1416179411
1416179411
1416179411
1416179411
1416179411
1416179411
1416179411
1416179411

12.单例模式(枚举完善)

完善使用enum枚举实现单例模式,不暴露枚举类实现细节的封装代码
示例:

/**
 * 完善使用enum枚举实现单例模式,不暴露枚举类实现细节的封装代码
 * @author bruceliu
 * @create 2019-02-28 21:44
 */
public class ClassFactory {

    private enum MyEnumSingleton {

        singletonFactory;

        private MySingleton instance;

        private MyEnumSingleton() {//枚举类的构造方法在类加载是被实例化
            instance = new MySingleton();
        }

        public MySingleton getInstance() {
            return instance;
        }
    }

    public static MySingleton getInstance() {
        return MyEnumSingleton.singletonFactory.getInstance();
    }


}

class MySingleton {
    public MySingleton() {
    }
}

测试代码:

/**
 * 完善使用enum枚举实现单例模式,不暴露枚举类实现细节的封装代码
 * @author bruceliu
 * @create 2019-02-28 21:41
 */
public class TestMySingleton extends Thread {

    @Override
    public void run() {
        System.out.println(ClassFactory.getInstance().hashCode());
    }

    public static void main(String[] args) {

        TestMySingleton[] mts = new TestMySingleton[10];

        for (int i = 0; i < mts.length; i++) {
            mts[i] = new TestMySingleton();
        }

        for (int j = 0; j < mts.length; j++) {
            mts[j].start();
        }
    }
}

运行结果:

712355351
712355351
712355351
712355351
712355351
712355351
712355351
712355351
712355351
712355351

猜你喜欢

转载自blog.csdn.net/BruceLiu_code/article/details/88046915