工厂本身
配置文件
日历
ioc容器
上下文
都是单例
目的:为了使资源共享,只需要赋值或者初始化一次,大家都能够重复利用
一个类模板 在整个系统运行中只允许产生一个实例,有且只有一个
保证单例的技术方案有很多种
需要解决并发访问时的线程安全问题
1.饿汉式单例,不会产生线程安全问题
在实例使用之前不管你用不用,我都先new出来再说,避免了线程安全问题
优点:没有任何的锁,执行效率比较高
缺点:类在加载时便初始化,不管用不用都占用着空间,浪费了内存
public class Singleton { private Singleton() {};/** 先静态后动态* 先属性后方法* 先上后下*///static是静态,表名内存中只有这一个对象,//1、final修饰的类不能被继承。//2、Sting就是一个被final修饰的类,我们只能用,不用继承。//3、final不仅可以修饰类,还可以修饰变量,被final修饰的变量就是一个常量,只能赋值一次。private static final Singleton hungry=new Singleton();public static Singleton getInstance() {System.out.println(System.currentTimeMillis()+":"+hungry);return hungry;}}//饿汉式测试线程安全问题
public class TestSingleton { public static void main(String[] args) {//模拟多线程下的单例线程安全问题//CountDownLatch类似计数器,当count使用完之前,之前的代码会被拦截,当count用完之后,之前拦截的代码会一起运行CountDownLatch latch=new CountDownLatch(100);/*final Set<Singleton> syncSet=Collections.synchronizedSet(new HashSet<Singleton>());*/for(int i=0;i<100;i++) {new Thread() {public void run() {// syncSet.add(Singleton.getInstance());try {latch.await();Singleton.getInstance();}catch(Exception e) {e.printStackTrace();}}}.start();latch.countDown();}}}
//输出结果
1535792250971:com.gupaoedu.singleton.Singleton@56f433c1
1535792250971:com.gupaoedu.singleton.Singleton@56f433c1
1535792250971:com.gupaoedu.singleton.Singleton@56f433c1
1535792250971:com.gupaoedu.singleton.Singleton@56f433c1
1535792250971:com.gupaoedu.singleton.Singleton@56f433c1
1535792250971:com.gupaoedu.singleton.Singleton@56f433c1
1535792250971:com.gupaoedu.singleton.Singleton@56f433c1
同一时间点打印出来的对象都是一样的,说明在并发的情况下并未出现多个对象
2.懒汉式
//懒汉1,多线程情况下可能会造成线程不安全问题public class LazeOne {private LazeOne() {}private static LazeOne single = null;public static LazeOne getInstance() {if (single == null) {single = new LazeOne();}return single;}
}
//懒汉2为床建对象的方法加了同步锁,保证了安全但是降低了效率public class Lazy2 {private Lazy2() {}private static Lazy2 single=null;public static synchronized Lazy2 getInstance() {if (single == null) {single = new Lazy2();}return single; }
}
//懒汉3与懒汉2类似,减小了加锁的范围,提高了效率
在同步块中进行null值判断是因为
可能同一时间内多个线程通过第一次null值判断当第一个进入静态块的线程创建完对象后single便不再是null
,其他线程再进入静态块,便不能过第二次null值判断//如果没有第二次null值判断则同时通过第一次null值判断的线程都会创建一个对象
就不能保证单例了
//第一次null值判断是为了在创建对象之后不要其他线程进入静态块了,也就是说进入静态块的线程很少
保证了效率
public class Lazy3 {private Lazy3() {}private static Lazy3 single=null;public static Lazy3 getInstance() { if (single == null) {synchronized (Lazy3.class) {if (single == null) { single = new Lazy3();} } } return single; }
}
//懒汉4利用静态内部类延迟创建对象,还能保证单例
public class Lazy4 {//静态内部类只有在需要时才加载private static class LazyHolder {private static final Lazy4 INSTANCE = new Lazy4();}private Lazy4 (){}public static final Lazy4 getInstance() { return LazyHolder.INSTANCE;}}
3.注册登记式
public class RegisterMap { private static Map<String,Object> regist = new HashMap<String,Object>(); static {Object single = new Object();regist.put(single.getClass().getName(), single);}protected RegisterMap(){} /*** 每使用一次就会往map中存入一个对象,下次使用时直接从map中取* 传入类名,如果没传入类名则把自己注册进去,内存中只有一个regist,所以* 每个类只能有一个实例,ioc就是这个原理*/public static Object getInstance(String name) { if(name == null) { name = RegisterMap.class.getName(); } if(regist.get(name) == null) { try { regist.put(name, Class.forName(name).newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); }
}
return regist.get(name);
}
}