当前位置: 代码迷 >> 综合 >> 单例模式和原型模式
  详细解决方案

单例模式和原型模式

热度:39   发布时间:2023-10-24 05:46:26.0

工厂本身

配置文件

日历

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);  
}  
}

 

  相关解决方案