码迷,mamicode.com
首页 > 编程语言 > 详细

单例模式与多线程

时间:2019-03-04 19:24:46      阅读:201      评论:0      收藏:0      [点我收藏+]

标签:默认   ret   加载   延迟加载   懒汉模式   rac   nbsp   system   write   

1.饿汉模式

  该模式指调用方法前,实例已经被创建了。

/**
 * @author MM
 * @create 2019-03-04 16:39
 **/
public class MyObject {
    //立即加载模式 
    private static MyObject myObject = new MyObject();

    public MyObject() {
    }

    public static MyObject getInstance(){
        //
        return myObject;
    }
}

  

/**
 * @author MM
 * @create 2019-03-04 16:42
 **/
public class SingletonThread1 extends Thread {

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

    public static void main(String[] args) {
        SingletonThread1 t1 = new SingletonThread1();
        SingletonThread1 t2 = new SingletonThread1();
        SingletonThread1 t3 = new SingletonThread1();

        t1.start();
        t2.start();
        t3.start();
    }
}

  技术图片

该模式线程安全。

2. 懒汉模式(延迟加载)

  所谓延迟加载就是在调用获取实例方法时实例才被创建,常见的实例办法就是在获取实例时进行new 对象。

/**
 * @author MM
 * @create 2019-03-04 16:39
 **/
public class MyObject {
    //延迟加载模式
    private static MyObject myObject;

    public MyObject() {
    }

    public static MyObject getInstance(){
        if(myObject == null){
            myObject = new MyObject();
        }
        return myObject;
    }
}

  修改上面myObject代码,继续执行后结果,粗看结果是正确的,但稍微再次修改一下。

public class MyObject {
    //延迟加载模式
    private static MyObject myObject;

    public MyObject() {
    }

    public static MyObject getInstance() {
        try {
            if (myObject == null) {
                Thread.sleep(1000);
                myObject = new MyObject();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }
}

  技术图片

可见这种写法存在线程安全问题。

解决方案:

  1):synchronized同步方法

  

    public static synchronized MyObject getInstance() {
        try {
            if (myObject == null) {
                Thread.sleep(1000);
                myObject = new MyObject();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }

  这种方法虽然能获取正确的结果,但这种方法效率上稍微有些低下,因为整个方法同步,下一个线程要获得对象,需等待上一个线程释放锁后才可以继续执行。

  2):同步代码块

    a:如果直接将整个代码块同步其实效率和同步方法时一样的

    

    public static MyObject getInstance() {
        synchronized (MyObject.class){
            try {
                if (myObject == null) {
                    Thread.sleep(1000);
                    myObject = new MyObject();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return myObject;
    }

  

    b:针对某些重要代码进行单独同步

    public static MyObject getInstance() {
        try {
            if (myObject == null) {
                Thread.sleep(1000);
                synchronized (MyObject.class) {
                    myObject = new MyObject();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }

  技术图片

  只对创建实例的代码加锁,结果还是不正确的。

  3):使用双重检测锁

    public static MyObject getInstance() {
        try {
            if (myObject == null) {
                Thread.sleep(1000);
                synchronized (MyObject.class) {
                    if(myObject == null){
                        myObject = new MyObject();
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }

  技术图片

  表明上能达到线程安全。实际写法还是不对。

  4):静态内部类方法

    

public class MyObject {

    public MyObject() {
    }
    //静态内部类模式
    public static class MyObjectInstance{
        private static MyObject myObject = new MyObject();
    }

    public static MyObject getInstance() {

        return MyObjectInstance.myObject;
    }
}

  静态内部类能解决线程安全问题,但如果是遇到序列化对象时,使用这种默认方式运行得到的结果还是多实例的。

public class MyObject implements Serializable{

    private static final long serialVersionUID = -245041196348963545L;

    public MyObject() {
    }
    //静态内部类模式
    public static class MyObjectInstance{
        private static MyObject myObject = new MyObject();
    }

    public static MyObject getInstance() {

        return MyObjectInstance.myObject;
    }
}

  

public class SingletonSerializalbe {

    public static void main(String[] args) {
        MyObject myObject = MyObject.getInstance();

        //序列化
        try {
            FileOutputStream fos = new FileOutputStream(new File("D:\\test.txt"));
            ObjectOutputStream outputStream = new ObjectOutputStream(fos);
            outputStream.writeObject(myObject);
            outputStream.close();
            fos.close();
            System.out.println(myObject.hashCode());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //反序列化
        try {
            FileInputStream fins = new FileInputStream(new File("D:\\test.txt"));
            ObjectInputStream inputStream = new ObjectInputStream(fins);
            MyObject myObject1 = (MyObject) inputStream.readObject();
            inputStream.close();
            fins.close();
            System.out.println(myObject1.hashCode());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

  技术图片

可见反序列化后的对象和原来的对象是不一致,解决需要在对象中添加readResolve()方法 

public class MyObject implements Serializable{

    private static final long serialVersionUID = -245041196348963545L;

    public MyObject() {
    }
    //静态内部类模式
    public static class MyObjectInstance{
        private static MyObject myObject = new MyObject();
    }

    public static MyObject getInstance() {

        return MyObjectInstance.myObject;
    }

    protected Object readResolve(){
        System.out.println("readResolve...");
        return MyObjectInstance.myObject;
    }
}

  技术图片

对于Serializable and Externalizable classes,方法readResolve允许class在反序列化返回对象前替换、解析在流中读出来的对象。实现readResolve方法,一个class可以直接控制反序化返回的类型和对象引用。 
方法readResolve会在ObjectInputStream已经读取一个对象并在准备返回前调用。ObjectInputStream 会检查对象的class是否定义了readResolve方法。如果定义了,将由readResolve方法指定返回的对象。
5):使用static代码块实现单例模式
  
public class MyObject implements Serializable{

    private static final long serialVersionUID = -245041196348963545L;

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

    public static MyObject getInstance() {

        return instance;
    }
}

  6):使用枚举类实现单例

  


public class MyObject {


public enum MyObjectEnum {

MY_OBJECT_ENUM;
private MyObject instance = null;
MyObjectEnum() {
this.instance = new MyObject();
}


public MyObject getInstance(){
return instance;
}
}

public static MyObject getInstance(){
return MyObjectEnum.MY_OBJECT_ENUM.getInstance();
}
}
 
public class SingletonThread1 extends Thread {

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

    public static void main(String[] args) {
        SingletonThread1 t1 = new SingletonThread1();
        SingletonThread1 t2 = new SingletonThread1();
        SingletonThread1 t3 = new SingletonThread1();

        t1.start();
        t2.start();
        t3.start();
    }
}

技术图片

枚举和static块类似,天然能保证线程安全。初始化时构造函数先被执行,该方法由jvm保证同步,效率很高。

 

单例模式与多线程

标签:默认   ret   加载   延迟加载   懒汉模式   rac   nbsp   system   write   

原文地址:https://www.cnblogs.com/gcm688/p/10472194.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!