单例模式与多线程

Posted Java与大数据学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式与多线程相关的知识,希望对你有一定的参考价值。

参考书籍《Java多线程编程核心技术》

通过单例模式与多线程技术相结合,在这个过程中发现很多以前从未考虑过的情况,一些不良的程序设计方法如果应用到商业项目中,将会遇到非常大的麻烦。所以要考虑一件事,如何使单例模式遇到多线程是安全的,正确的。

立即加载/“饿汉模式”

立即加载就是使用类的时候已经将对象创建完毕,常见的实现方法就是直接new实例化。

class MyObject{
    //立即加载
    private static MyObject myObject = new MyObject();
    private MyObject(){}
    public static MyObject getInstance(){
        //缺点是不能有其他实例变量
        //因为getInstance()方法没有同步
        //所以可能出现非线程安全问题
        return myObject;
    }
}

延迟加载/“懒汉模式”

延迟加载就是在调用get()方法时实例才被创建,常见的实现方法是在get()方法中进行new实例化。

class MyObject{
    private static MyObject myObject;
    private MyObject(){}
    //设置同步方法效率太低了
    //整个方法被上锁
    synchronized public static MyObject getInstance(){
        try {
            if(myObject!=null){

            }else{
                //模拟在创建对象之前做的一些准备性工作
                Thread.sleep(3000);
                myObject = new MyObject();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }
}

使用DCL双检查锁机制

DCL是大多数多线程结合单例模式使用的解决方案

class MyObject{
    private volatile static  MyObject myObject;
    private MyObject(){}
    //使用双检查机制解决问题,既保证了不需要同步代码块的异步性
    //又保证了单例的效果
    public static MyObject getInstance(){
        try {
            if(myObject != null){

            }else{
                //模拟在创建对象之前做的一些准备性工作
                Thread.sleep(3000);
                synchronized (MyObject.class){
                    if(myObject ==null){
                        myObject = new MyObject();
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }
}

使用静态内部类

class MyObject{
    //内部类方式
    private static class MyObjectHandler{
        private static MyObject myObject = new MyObject();
    }
    private MyObject(){}
    public static MyObject getInstance(){
        return MyObjectHandler.myObject;
    }
}

序列化与反序列化的单例模式实现

静态内部类可以达到线程安全问题,但如果遇到序列化对象时,得到的结果还是多例的。

测试代码:

package com.sixj.Thread;

import java.io.*;

/*
  Created by sixj 2019/3/12 20:45
*/

public class SaveAndRead {
    public static void main(String[] args) {
        try {
            MyObject myObject = MyObject.getInstance();
            FileOutputStream fos = new FileOutputStream(new File("myObjectFile.txt"));
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(myObject);
            oos.close();
            fos.close();
            System.out.println(myObject.hashCode());
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            FileInputStream fis = new FileInputStream(new File("myObjectFile.txt"));
            ObjectInputStream ois = new ObjectInputStream(fis);
            MyObject myObject = (MyObject)ois.readObject();
            ois.close();
            fis.close();
            System.out.println(myObject.hashCode());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

运行结果:

所以要做一下修改,在静态内部类的基础上加一个readResolve()方法

class MyObject implements Serializable {
    //内部类方式
    private static class MyObjectHandler{
        private static MyObject myObject = new MyObject();
    }
    private MyObject(){}
    public static MyObject getInstance(){
        return MyObjectHandler.myObject;
    }
    protected Object readResolve() throws ObjectStreamException {
        System.out.println("调用了readResolve方法!");
        return MyObjectHandler.myObject;
    }
}

运行结果:

使用static代码块实现

静态代码块中的代码在使用类的时候就已经执行了,所以可以应用静态代码块的这个特性来实现单例模式。

class MyObject{
    private static MyObject instance = null;
    private MyObject(){}
    static {
        instance = new MyObject();
    }
    public static MyObject getInstance(){
        return instance;
    }

}

使用enum枚举数据类型实现

枚举enum和静态代码块的特性相似,在使用枚举类时,构造方法会被自动调用,也可以应用其这个实现单例模式。

通过enum枚举获得单例的Connection对象:

class Connection {}

class MyObject{
    public enum MyEnumSingleton{
        connectionFactory;
        private Connection connection;
        private MyEnumSingleton(){
            connection = new Connection();
        }
        public Connection getConnection(){
            return connection;
        }
    }
    public static Connection getConnection(){
        return MyEnumSingleton.connectionFactory.getConnection();
    }
}

测试:

class S_Thread extends Thread{
    @Override
    public void run() {
        for(int i = 0; i < 5; i++){
        System.out.println(MyObject.getConnection().hashCode());
        }
    }
}
public class TestSingleton {
    public static void main(String[] args) {
        S_Thread s1 = new S_Thread();
        S_Thread s2 = new S_Thread();
        S_Thread s3 = new S_Thread();
        s1.start();
        s2.start();
        s3.start();
    }
}

运行结果:



以上是关于单例模式与多线程的主要内容,如果未能解决你的问题,请参考以下文章

单例模式与多线程

Java - 单例模式与多线程

并发编程:单例与多线程

彻头彻尾理解单例模式与多线程

彻头彻尾理解单例模式与多线程

Java多线程核心技术单例模式与多线程