单例模式

单例模式的概念及应用

单例类需要满足以下几个点

  • 1.单例类只能有一个实例
  • 2.实例只能自己生成
  • 3.像其他所有对象提供这一实例

单例模式的应用

  • 多线程情况下保证资源的一致性,例如多台打印机打印一个文件。

单例模式的写法

饿汉式

线程安全

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

饿汉式就是在调用getInstance这个方法的时候实例直接被创建,并不进行判断实例是否为空。由于实例是被直接创建,所以并不可以延迟加载(Lazy Loading),容易产生垃圾对象。

还有一个问题,饿汉式是如何保证线程安全的?

假如线程1实例化一个EagerSingleton的同时线程2也在实例化,那么对象不就产生两个内存地址了嘛?事实上是java虚拟机帮我们解决了这个问题。

JVM的解决办法

当jvm遇到new这个关键字的时候,首先会检查符号引用是否已经存在常量池中,(这句话的意思就是新创建的对象是否已经存在),如果没有的话就开始为新建的对象分配内存。

分配内存有两种方式:假如内存规整,就用第一种指针碰撞(Bump the Pointer),就是向空闲的内存区挪动一段位移。假如内存不规整,就采用空闲列表(Free List),从列表上选取一段足够大的内存。

对象创建是很频繁的行为,如果线程1给A分配内存的时候,线程2也在给B分配内存,无论采取何种方式分配内存,都有可能将B的内存分配给A。

解决问题的两种方案:一种是对分配动作同步处理,采用CAS配上失败重试的方式保证更新操作的原子性。另一种将先对线程分配空间,在他们自己的空间里进行操作,称之为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB),分配空间用完时,才需要同步锁定。

懒汉式

顾名思义,是可以Lazy Loading的,但是线程不安全

public class LazySingleton
{
    private static volatile LazySingleton instance = null;
    
    private LazySingleton(){}
    public static LazySingleton getInstance()
    {
        if (instance == null)
            instance = new LazySingleton();
        return instance;
    }
}

双检索

线程安全,Lazy Loading

public class DoubleCheckLockSingleton
{
    private static DoubleCheckLockSingleton instance = null;
    
    private DoubleCheckLockSingleton(){}
    
    public static DoubleCheckLockSingleton getInstance()
    {
        if (instance == null)
        {
            synchronized (DoubleCheckLockSingleton.class)
            {
                if (instance == null)
                    instance  = new DoubleCheckLockSingleton();
            }
        }
        return instance;
    }
}

单例模式的应用之Mybatis

Mybatis中事务是通过SqlSession来处理的,每次应用想要访问数据库,都需要通过SqlSessionFactory来创建SqlSesson。

如果多次创建同一个数据库的SqlSessionFactory,就会打开更多数据库连接,数据库连接资源很快就会耗尽。

所以一个数据库只对应一个SqlSessionFactory,那么肯定想到单例模式。

public class SqlSessionFactoryUtil {
	//SqlSessionFacory对象
	private static SqlSessionFactory sqlSessionFactory;
	//类线程锁
	private static final Class CLASS_LOCK = SqlSessionFactoryUtil.class;
	//私有化构造参数
	private SqlSessionFactoryUtil() {}
	
	public static SqlSessionFactory initSqlSessionFactory(){
		String resource = "mybatis-config.xml";
		InputStream inputStream = null;
		try {
			inputStream = Resources.getResourceAsStream(resource);
		} catch (IOException e) {
			Logger.getLogger(SqlSessionFactoryUtil.class.getName()).log(Level.SEVERE,null,e);
		}
		synchronized (CLASS_LOCK) {
			if (sqlSessionFactory == null) {
				sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			}
		}
		return sqlSessionFactory;
	}
    /*
     * 打开SqlSession
     */
	public static SqlSession openSqlSession(){
		if (sqlSessionFactory == null) {
			initSqlSessionFactory();
		}
		return sqlSessionFactory.openSession();
	}
}
posted @ 2018-11-14 11:05  bihang  阅读(209)  评论(0编辑  收藏  举报