概述
单例:保证一个对象永远只能有一个实例存在
应用:
—数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗
—多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
—Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
—应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
单例的实现方法:私有的静态实例对象,私有的构造函数,提供公开的静态方法来获取私有的静态实例对象
饿汉模式
在类加载的时候,就创建的对象。能够保证永远都只有一个实例,但是会占用内存,但由于现在硬件价格降低,占用这点内存没啥事儿。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
System.out.println("饿汉模式:构造函数被调用= " + this.hashCode());
}
public static Singleton getIntance() {
return instance;
}
}
懒汉模式
方法访问的时候才创建对象。单线程没有问题,但多线程情况下,不能保证永远只产生一个对象
private static LazySingleton instance;
private LazySingleton(){
}
/*
* 懒汉模式:单线程没有问题,但是多线程的时候,不能保证永远只有一个实例
*/
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
/*
* 对方法加锁会造成非常严重的性能影响
*/
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
/**
* 在判断null后加锁把实例化代码锁住。看似解决了多线程问题。其实不然 :
* 设现有线程A和B,在t1时刻线程A和B均已通过判空语句但都未取得锁资源;
* t2时刻时,A先取得锁资源进入临界区(被锁的代码块),执行new操作创建实例对象,然后退出临界区,释放锁资源。
* t3时刻,B取得被A释放的锁资源进入临界区,执行new操作创建实例对象,然后退出临界区,释放锁资源。
* 明显地,Singleton被实例化两次。这样写也不能保证线程安全。
public static LazySingleton getInstance2() {
if (instance == null) {
synchronized (LazySingleton .class) {
instance = new LazySingleton ();
}
}
return instance;
}
}
静态内部类
能够保证永远只产生一个实例
public class StaticInnerSingleton {
private StaticInnerSingleton(){
System.out.println("StaticInnerSingleton,调用构造函数:");
}
public static final StaticInnerSingleton getInstance() {
return SingletionHolder.instance;
}
private static final class SingletionHolder{
private static final StaticInnerSingleton instance = new StaticInnerSingleton();
}
}
枚举类
---------------
测试类
public class MainTest {
public static void main(String args[]) {
Runnable run = new Runnable() {
public void run() {
// 饿汉模式:在类加载的时候,就new了实例
HungreSingleton.getIntance();
// 懒汉模式:会有线程安全问题,多线程情况下会重复创建对象
LazySingleton.getInstance();
// 加锁模式:方法加锁
LockLazySingleton.getInstance();
// 双重加锁:
LockLazySingleton.getInstance2();
// volitaile关键字:对象上加锁volatile,对象中加 synchronized 依然不能保证永远是单一对象
LockLazySingleton.getInstance3();
// 枚举
// EnumSingleton.instance;
// 静态内部类
StaticInnerSingleton.getInstance();
}
};
for(int i = 1; i<1000;i++) {
Thread th = new Thread(run);
th.start();
}
}
}
总结:
一般使用饿汉模式,静态内部类。