java设计模式单例模式

 创建型模式: – 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。

• 结构型模式: – 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模 式。

• 行为型模式: – 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模 式、解释器模式、状态模式、策略模式、职责链模式、访问者模式

单例模式:

• 核心作用: – 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

• 常见应用场景:

– Windows的Task Manager(任务管理器)就是很典型的单例模式 – windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

– 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取。

– 网站的计数器,一般也是采用单例模式实现,否则难以同步。

– 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作 ,否则内容不好追加。

– 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。

– 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。

– Application 也是单例的典型应用

– 在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理 – 在servlet编程中,每个Servlet也是单例 – 在spring MVC框架/struts1框架中,控制器对象也是单例

单例模式的优点:

– 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要 比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动 时直接产生一个单例对象,然后永久驻留内存的方式来解决 – 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计 一个单例类,负责所有数据表的映射处理

 常见的五种单例模式实现方式:

– 主要: • 饿汉式(线程安全,调用效率高。 但是,不能延时加载。)

• 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)

– 其他: • 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用)

• 静态内部类式(线程安全,调用效率高。 但是,可以延时加载)

• 枚举单例(线程安全,调用效率高,不能延时加载)

 

饿汉式实现(单例对象立即加载)

//饿汉式单例
public class Singleton01 {
	//类初始化时,立即加载这个对象
	//加载类是天然的线程安全的,(没有延时加载的优势)
   private static Singleton01 instance=new Singleton01();
   private Singleton01(){
	   
   }
   //方法没有同步,调用效率高
   public static Singleton01 getInstance(){
	   return instance;
   } 
}

  懒汉式

//懒汉式
public class Singleton02 {
	//类初始化时,不初始化这个对象(真正用到的时候在创建)
    private static Singleton02 instance;
    private Singleton02(){  //私有构造器
    	
    }
    //方法同步,调用效率低
    public static synchronized Singleton02 getInstance(){
    	if(instance==null){
    		instance=new Singleton02();
    	}
    	return instance;
    }
}

  双重检索:

//双重检测所机制
//由于编译器优化原因和jvm底层内部模型原因,有时候回出现问题
public class Singleton03 {
	private static Singleton03 instance=null;
	
	public static Singleton03 getInstance(){
		if(instance==null){
			Singleton03 sc;
			synchronized (Singleton03.class){
				sc=instance;
				if(sc==null){
					synchronized(Singleton03.class){
						if(sc==null){
							sc=new Singleton03();
						}
					}
					instance=sc;
				}
			}
		}
		return instance;
	}
	private Singleton03(){
		
	}
}

  静态内部类:

//静态内部类
public class Singleton04 {
	 //要点:
	//– 外部类没有static属性,则不会像饿汉式那样立即加载对象。
	//– 只有真正调用getInstance(),才会加载静态内部类。加载类时是线程 安全的。 
	//instance是static final 类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,
	//从而保证了线程安全性. – 兼备了并发高效调用和延迟加载的优势!

	private static class Singleton04ClassInstance {
		private static final Singleton04 instance = new Singleton04();
	}
	private Singleton04() {

	}
	public static  Singleton04 getInstance() {
		return Singleton04ClassInstance.instance;
	}	
}

  枚举模式:

//枚举单例(没有延时加载)
public enum Singleton05 {
    //定义一个枚举,枚举 元素本身就是一个单例
	INSTANCE;
	//添加自己需要的元素
	public void singletonOperation(){
		
	}
}

  

public static void main(String[] args) {
		Singleton01 s1=Singleton01.getInstance();
		Singleton01 s2=Singleton01.getInstance();
		System.out.println(s1);
		System.out.println(s2);
	}

  

 常见的五种单例模式实现方式

– 主要: • 饿汉式(线程安全,调用效率高。 但是,不能延时加载。) • 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)

– 其他: • 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用) • 静态内部类式(线程安全,调用效率高。 但是,可以延时加载) • 枚举式(线程安全,调用效率高,不能延时加载。并且可以天然的防止反射和反序列 化漏洞!) • 如何选用? – 单例对象  占用  资源 少,不需要  延时加载: • 枚举式   好于   饿汉式 – 单例对象  占用  资源 大,需要 延时加载: • 静态内部类式   好于  懒汉式

 反射和序列化破坏单例:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

public class SingletonTest01 {
	public static void main(String[] args) throws Exception{
		Singleton06 s1=Singleton06.getInstance();
		Singleton06 s2=Singleton06.getInstance();
		
		System.out.println(s1);
	    System.out.println(s2);
	    
	    //通过反射的方式直接调用私有构造器
		Class<Singleton06> clazz= (Class<Singleton06>)Class.forName("demo.singleton.Singleton06");
		//获取无参数构造器
		Constructor<Singleton06> c=clazz.getDeclaredConstructor(null);
		//跳过权限的检查
		c.setAccessible(true);
		Singleton06 s3=c.newInstance();
		Singleton06 s4=c.newInstance();
		
		System.out.println(s3);
		System.out.println(s4);
		
		//通过序列化的方式构造多个对象
		FileOutputStream fos=new FileOutputStream("d:/a.txt");
		ObjectOutputStream oos=new ObjectOutputStream(fos);
		oos.writeObject(s1);
		oos.close();
		fos.close();
		
		ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/a.txt"));
		Singleton06 s5=(Singleton06) ois.readObject(); 
		System.out.println(s5);
	}
}

  

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

public class SingletonTest01 {
	public static void main(String[] args) throws Exception{
		Singleton06 s1=Singleton06.getInstance();
		Singleton06 s2=Singleton06.getInstance();
		
		System.out.println(s1);
	    System.out.println(s2);
	    
	    //通过反射的方式直接调用私有构造器
		Class<Singleton06> clazz= (Class<Singleton06>)Class.forName("demo.singleton.Singleton06");
		//获取无参数构造器
		Constructor<Singleton06> c=clazz.getDeclaredConstructor(null);
		//跳过权限的检查
		c.setAccessible(true);
		Singleton06 s3=c.newInstance();
		Singleton06 s4=c.newInstance();
		
		System.out.println(s3);
		System.out.println(s4);
		
		//通过序列化的方式构造多个对象
		FileOutputStream fos=new FileOutputStream("d:/a.txt");
		ObjectOutputStream oos=new ObjectOutputStream(fos);
		oos.writeObject(s1);
		oos.close();
		fos.close();
		
		ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/a.txt"));
		Singleton06 s5=(Singleton06) ois.readObject(); 
		System.out.println(s5);
	}
}

  测试:

import java.util.concurrent.CountDownLatch;

public class SingletonTest03 {
	public static void main(String[] args) throws Exception {
		long start=System.currentTimeMillis();
		int threanNum=10;
		final CountDownLatch countDownLath=new CountDownLatch(threanNum);
				
		
		for(int i=0;i<10;i++){
			new Thread(new Runnable(){
				@Override
				public void run(){
					for(int i=0;i<1000000;i++){
						//Object o=Singleton04.getInstance();
						Object o=Singleton05.INSTANCE;
					}
					countDownLath.countDown();
				}
			}).start();
		}
		countDownLath.await();//mian线程阻塞,知道计数器变成0,才会继续往下执行
		long end=System.currentTimeMillis();
		System.out.println("总耗时:"+(end-start));
	}
}

  多线程情况的下的单例模式:

//标准的单例模式
public class SingletonDemo {
    private static volatile SingletonDemo instance=null;
    private SingletonDemo(){
        System.out.println(Thread.currentThread().getName()+
                "\t 我是构造方法SingletonDemo()");
    }
    //双端检索机制
    public static  SingletonDemo getInstance(){
        if(instance==null){
            synchronized (SingletonDemo.class){
                if(instance==null)
                    instance=new SingletonDemo();
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        for(int i=0;i<10;i++){
            new Thread(()->{
             SingletonDemo.getInstance();
            },String.valueOf(i)).start();
        }
    }
}

  

posted @ 2019-11-11 21:37  石shi  阅读(151)  评论(0编辑  收藏  举报