Java 单例模式
来源:互联网 发布:mac后台程序怎么关 编辑:程序博客网 时间:2024/06/05 11:58
概述
单例,为什么要使用单例?主要考虑以下两种情况
- 实例对象比较占用资源,例如内存等,所以保持系统中只有一个实例对象,能有效的避免内存浪费,前提是你得业务逻辑允许你使用同一个对象实例。实际案例,JDBC 中数据库连接池。
- 实例对象使用场景比较频繁,而且可以使用同一对象。如果你每次都去 New 一个出来,浪费内存是一个问题,同时也浪费了代码的执行时间。
今天拿出 Java 中常用的单例模式进行讲解,因为这个也是面试(校招类面试)经常叫你写的。
饿汉式单例
public class HungrySingle {private static HungrySingle single =new HungrySingle() ;private HungrySingle(){ System.out.println("HungrySingle.HungrySingle ");}public static HungrySingle getInstance(){ System.out.println("HungrySingle.getInstance"); return single;}public static void main(String[] args) { HungrySingle hungrySingle = getInstance();} }
这种单例利用静态变量初始化的特点,会在第一次调用这个类的时候,自动进行初始化。
打印结果
HungrySingle.HungrySingle HungrySingle.getInstance
由于使用的是静态初始化机制,所以不存在线程安全问题,但是这个单例叫做饿汉单例,也就是说,可能存在这种情况,单例被实例化了但是却没有被调用,会造成浪费。同时另一个问题就是,构造方法里面如果执行的初始化操作较多,或者代码操作比较耗时,这样的话,单例实例化执行的速度会受到影响。
懒汉式单例
懒汉式单例可以避免饿汉式单例的问题,能够保证在第一次使用的时候初始化对象
public class LazySingle {private static LazySingle single;private LazySingle(){}public synchronized LazySingle getInstance(){ if (single == null){ single= new LazySingle(); } return single;}}
这种懒汉式不仅能避免浪费的问题,同时使用了 synchronized 关键字,所以是线程安全的。但是线程安全的牺牲代价就是,代码执行效率不高,因为每次只允许一个线程执行获取单例对象的代码,所以对多线程高效访问的场景基本不适用。
tip: synchroized 修饰的是 getInstance() 方法,在一个线程访问这个方法的时候,其他线程不能访问LazySingle 类的这个 getInstance() 方法,但是可以访问其他非 synchronized 修饰的方法。
double - check 的单例模式
还是从线程安全考虑,不过我们将 synchronized 的代码粒度变小,因为 synchronized 可以修饰方法,也可以修饰代码块,分别称为同步方法和同步代码块,同步代码块能够让我们更加有目的的对代码进行线程安全控制。下面的单例模式就是这种用法,称为 double - check 的单例模式。
public class SingleTon {private volatile static SingleTon singleTon;private SingleTon() {}public static SingleTon getInstance(){ if(singleTon == null) synchronized (SingleTon.class) { if(singleTon == null){ singleTon = new SingleTon(); } } return singleTon;}}
这个代码是线程安全的,而且它的线程同步机制,仅仅是在单例对象没有被创建的时候有效,所以基本不会牺牲代码的执行性能。
这里我还使用了 volatile 关键字,这里 volatile 关键字是一种轻量级的 synchronized ,它在多处理器开发中保证了共享变量的可见性,也即是说,当一个线程修改了 volatile 共享变量之后,另一个线程读到的是修改后的值。
static 机制的单例
这种单例模式,例如了 static 机制,和内部类的特点
public class StaticSingle {private StaticSingle(){}public static StaticSingle getInstance(){ return SingleHolder.staticSingle;}private static class SingleHolder { private static StaticSingle staticSingle = new StaticSingle();}}
这种单例是线程安全的,同时代码的执行性能基本是不受影响的,也能保证使用的时候,才会进行初始化。这种实现机制的意思是,参看下面的代码
public class StaticSingle {static {new MyInnerClass(); }private StaticSingle(){ System.out.println("StaticSingle.StaticSingle");}public static StaticSingle getInstance(){ System.out.println("StaticSingle.getInstance"); return SingleHolder.staticSingle;}private static class SingleHolder { private static StaticSingle staticSingle = new StaticSingle(); private SingleHolder(){ System.out.println("SingleHolder.SingleHolder"); }}public static class MyInnerClass{ public MyInnerClass(){ System.out.println("MyInnerClass.MyInnerClass"); }}public void foo(){ System.out.println("StaticSingle.foo");}public static void fooS(){ System.out.println("StaticSingle.fooS");}public static void main(String[] args) { //fooS(); new StaticSingle().foo();}}
执行 main 方法中 的 foos() 方法打印如下
MyInnerClass.MyInnerClassStaticSingle.fooS
执行 main 方法中 new StaticSingle().foo() 打印如下
MyInnerClass.MyInnerClassStaticSingle.StaticSingleStaticSingle.foo
所以这个类第一次创建的时候,内部静态类并不会被创建,之前饿汉式的单例是,只要这个类被第一次访问了,就一定会执行单例对象的实例化操作。所以很好的避免了浪费问题。
关于单例对象被回收
单例对象基本是静态变量,在 Java 中对静态变量的回收是很谨慎的,所以基本可以认为不会主动被 GC 回收掉。
关于更深层次的线程安全问题
参考这篇文章
关于对象初始化延迟的问题。
- java单例模式
- Java单例模式
- java单例模式
- java单例模式
- Java单例模式
- Java单例模式
- java单例模式
- Java单例模式
- java 单例模式
- java单例模式
- java单例模式
- java单例模式
- java 单例模式
- JAVA单例模式
- java单例模式 .
- Java 单例模式
- Java单例模式
- Java 单例模式
- Mapped Statements collection already contains value for
- mysql 性能优化点记录
- MySQL中利用外键实现级联删除、更新
- 有序数组合并
- 浅析VO、DTO、DO、PO的概念、区别和用处
- Java 单例模式
- Google软件测试之道
- 树莓派安装pidora (Fedora Remix)详细教程 及一系列设置
- Qt调用dll的步骤
- JAX-RS @QueryParam example
- nginx File not found.
- Sqlite 数据库存储
- UVA 253 Cube painting(思维题)
- C# PPT 为形状设置三维效果