搜公众号
推荐 原创 视频 Java开发 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库
Lambda在线 > Java与大数据学习 > 单例模式与多线程

单例模式与多线程

Java与大数据学习 2019-03-15
举报

参考书籍《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与大数据学习」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

举报