目录
单例模式
饿汉式单例
懒汉式单例
内部类单例
注册登记式单例
枚举式单例
单例模式(一个类模板,在整个系统执行过程中,只允许产生一个实例)应用广泛,主要应用在:
单例模式:解决一个并发访问的时候线程安全问题,保证单例的技术方案有很多种
在实例使用之前,不管你用不用,我都先new出来再说,避免了线程安全问题
饿汉式单例:
public class Hungry {
//私有构造方法,防止外部new
private Hungry(){}
private static final Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
}
我们来测试:
public static void main(String[] args) {
int count = 200;
//发令枪,我就能想到运动员
final CountDownLatch latch = new CountDownLatch(count);
long start = System.currentTimeMillis();
for (int i = 0; i < count;i ++) {
new Thread(){
@Override
public void run() {
try{
try {
// 阻塞
// count = 0 就会释放所有的共享锁
// 万箭齐发
latch.await();
}catch(Exception e){
e.printStackTrace();
}
//必然会调用,可能会有很多线程同时去访问getInstance()
Object obj = Hungry.getInstance();
System.out.println(System.currentTimeMillis() + ":" + obj);
}catch (Exception e){
e.printStackTrace();
}
}
}.start(); //每循环一次,就启动一个线程,具有一定的随机性
//每次启动一个线程,count --
latch.countDown();
}
long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start));
}
打印结果发现,无论怎么运行,程序始终拿到的是一个实例
默认加载的时候不实例化,在需要用到这个实例的时候进行实例化(延时加载)
懒汉式单例:
public class LazyOne {
private LazyOne(){}
//静态块,公共内存区域
private static LazyOne lazy = null;
public static LazyOne getInstance(){
//调用方法之前,先判断
//如果没有初始化,将其进行初始化,并且赋值
//将该实例缓存好
if(lazy == null){
//两个线程都会进入这个if里面
lazy = new LazyOne();
}
//如果已经初始化,直接返回之前已经保存好的结果
return lazy;
}
}
打印结果发现存在不同实例,说明饿汉式单例是线程不安全!
怎么变为安全的呢!我们通常可以这样设计饿汉式单例(在获取实例方法上加上synchronized 锁)
public class LazyTwo {
private LazyTwo(){}
private static LazyTwo lazy = null;
public static synchronized LazyTwo getInstance(){
if(lazy == null){
lazy = new LazyTwo();
}
return lazy;
}
}
然后我们将测试工具类实例改为:
运行发现,无论怎么运行,程序始终拿到的是一个实例,说明synchronized 锁是可以保证线程安全的!
但是synchronized 性能并不是最好的锁!
我们看这样的测试:
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 200000000;i ++) {
Object obj = LazyTwo.getInstance();
}
long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start));
}
运行发现每次获取的时间性能较低,由此产生另一种单例,内部类单例模式
特点:
// 史上最牛B的单例模式的实现方式
public class LazyThree {
private boolean initialized = false;
//每一个关键字都不是多余的
//static 是为了使单例的空间共享
//保证这个方法不会被重写,重载
public static final LazyThree getInstance(){
//在返回结果以前,一定会先加载内部类
return LazyHolder.LAZY;
}
//默认不加载
private static class LazyHolder{
private static final LazyThree LAZY = new LazyThree();
}
}
基于map形式的注册单例:
public class RegisterMap {
private RegisterMap(){}
private static Map register = new ConcurrentHashMap();
public static RegisterMap getInstance(String name){
if(name == null){
name = RegisterMap.class.getName();
}
if(register.get(name) == null){
try {
register.put(name, new RegisterMap());
}catch(Exception e){
e.printStackTrace();
}
}
return (RegisterMap)register.get(name);
}
}
使用常量值来保证对象的唯一,实际上就是注册登记式的一种
public enum DataSourceEnum {
DATASOURCE;
private DBConnection connection = null;
private DataSourceEnum() {
connection = new DBConnection();
}
public DBConnection getConnection() {
return connection;
}
}