这里写自定义目录标题
- 一 单例模式
- 1 静态常量饿汉式
- 2 静态代码块饿汉式
- 3 线程不安全懒汉式
- 4 线程安全懒汉式
- 线程安全,同步方法
- 线程安全,同步代码块
- 5 doubleCheck(双重检查,推荐使用)
- 6 静态内部类
- 7 枚举
一 单例模式
单例模式就是保证在某个软件系统中,对某个类只存在一个对象实例,并且·该类只存在一个取得其对象实例的方法(静态方法)
- 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使 用单例模式可以提高系统性能
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
- -单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级 对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)
1 静态常量饿汉式
public class SingletonTest01 {
public static void main(String[] args) {
Singleton instance=Singleton.getInstance();
Singleton instance2=Singleton.getInstance();
//比较地址的值
System.out.println(instance2==instance);
//比较两者的HashCode
System.out.println("instance2.hashcode:"+instance2.hashCode());
System.out.println("instance.hashCode:"+instance.hashCode());
}
}
//饿汉式:静态变量
class Singleton{
//1.构造器私有化,外部不能new出来
private Singleton(){
}
//2.本类内部创建一个对象实例
private final static Singleton instance=new Singleton();
//3 提供一个公有的静态对象,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
运行结果可以看出来,hashcode的值是一样的,说明这两个对象实例实际上是一个对象实例
优点:
- 写法比较简单,在类装载时候就完成了实例化,避免了线程同步的问题
缺点:
- 在类加载的时候就完成实例化,没有达到懒加载(Lazy loading)的效果,如果这个实例从始至终都没有被调用,就会造成内存的浪费
- 虽然采用classloder机制避免了多线程的同步问题,但是导致类装载的方式有很多种,不确定是否因为其他方式导致类装载
总结:
- 这种单例模式可以用,但是会造成内存的浪费
2 静态代码块饿汉式
//饿汉式:静态代码块的方式
public class Singleton{
//1.构造器私有化,外部不能new出来
private Singleton(){
}
//2.本类内部创建一个对象实例
private static Singleton instance;
static {
instance=new Singleton();
}
//3 提供一个公有的静态对象,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
优点:类实例化的过程,放在了静态代码块中执行,静态代码块也是在类装载的时候执行的
结论:会造成内存浪费
java.lang.RunTime是最经典的饿汉式,单例模式
3 线程不安全懒汉式
功能子类,本类内部创建对象实例
package com01.xyt;
public class Singleton {
// 构造器私有化,外部不能new
private static Singleton instance;
//本类内部创造对象实例
private Singleton(){
}
// 提供一个公有的静态方法,使用到该方法时,才创建instance,懒汉,不用不创建
public static Singleton getInstance(){
if (instance==null){
instance=new Singleton();
}
return instance;
}
}
优点:
- 确实起到了LazyLoading的效果,用到才创建,用不到不创建,只能在单线程条件下用
- 多线程下,一个线程进入了if判断语句块,还没来得及向下执行,另一个线程也通过了判断块,就会创造出多个实例,存在风险
- 实际开发时候,不要采用这种方式,存在风险
4 线程安全懒汉式
线程安全,同步方法
public class Singleton {
// 构造器私有化,外部不能new
private static Singleton instance;
//本类内部创造对象实例
private Singleton(){
}
// 提供一个静态的公有方法,使用到该方法时,才创建instance,
// 懒汉,不用不创建
//get方法,得到一个值
//加入同步关键字,多个线程排队
public static synchronized Singleton getInstance(){
if (instance==null){
instance=new Singleton();
}
return instance;
}
}
分析:
- 解决了线程不安全的问题,但是效率太低,每个线程想要获得类的实例的时候,执行getInstance()方法都要同步
- 但其实这个方法执行一次实例化代码就够了,后面想获得该类实例,直接return就行,方法同步效率太低,用了同步机制来解决
- 重量级同步关键字,sythonized
线程安全,同步代码块
package com01.xyt;
public class Singleton {
// 构造器私有化,外部不能new
private static Singleton singleton;
//本类内部创造对象实例
private Singleton(){
}
// 提供一个静态的公有方法,使用到该方法时,才创建instance,
// 懒汉,不用不创建
//get方法,得到一个值
//加入同步关键字,多个线程排队
public static Singleton getInstance(){
if (singleton==null){
synchronized (Singleton.class){
singleton=new Singleton();
}
}
return singleton;
}
}
说明:
- 这种方法实际上是一种伪同步,实际上并没有起到线程同步的作用
- 容易导致多个线程进入if判断,从而产生多个实例,效率低,实际开发中不能用
5 doubleCheck(双重检查,推荐使用)
package com01.xyt;
public class Singleton {
// 构造器私有化,外部不能new
private static volatile Singleton instance;
//本类内部创造对象实例
private Singleton(){
}
/**
* 提供了一个静态的公有方法去,加入双重检查代码,解决线程安全与懒加载问题
* 保证效率,推荐使用
* @return
*/
public static Singleton getInstance(){
//A B,控制进一个
if (instance==null){
//对Singleton进行同步
synchronized (Singleton.class){
//A进来,B进不去
if (instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
说明:
两个if判断
,一个不够用- 解决了线程安全与效率的问题,因此推荐使用
- 用了volatile,一种轻量级主存
- Double-Check概念是线程开发中经常使用到的,如图所示,进行了两次if(Singleton== null)的检查,可以保证线程安全
- 实例代码只执行一次,后面再访问时,判断if(singleton==null),直接return实例化对象
- 线程安全,延迟加载,效率较高
6 静态内部类
package com01.xyt;
public class Singleton {
// 构造器私有化,外部不能new
private static volatile Singleton instance;
//本类内部创造对象实例
private Singleton(){
}
//JVM装载内部类是安全的
//写一个静态内部类,这个类中有一个静态属性Singleton
private static class SingletonInstance{
private static final Singleton INSTANCE=new Singleton();
}
//提供一个静态的公有方法,直接返回成员变量
public static synchronized Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
说明:
- 外部类被装载时候,内部类不一定被装载
- 装载时候,线程是安全的,采用类装载机制,确保初始化实例时候,只有一个线程
- 静态内部类方法在Singleton类被装载时,并不会立即实例化,而是在需要实例化时候,调用getInstance方法,装载SingletonInstance类,从而完成Singleton的实例化
- 类的静态属性只会在第一次进行类的加载时候进行初始化,JVM帮助我们保证了线程的安全性,一次只能进入一个线程
- 避免了线程不安全,静态内部类实现延迟加载,效率高
用到静态实例才会加载
7 枚举
public class SingletonTest3 {
public static void main(String[] args) {
Singleton instance= Singleton.INSTANCE;
Singleton instance2= Singleton.INSTANCE;
//比较地址的值
System.out.println(instance2==instance);
//比较两者的HashCode
System.out.println("instance2.hashcode:"+instance2.hashCode());
System.out.println("instance.hashCode:"+instance.hashCode());
instance.sayOK();
}
}
enum Singleton{
INSTANCE;//属性
public void sayOK(){
System.out.println("-------OK");
}
}
说明:
- 枚举避免多线程同步问题,同时防止反序列化重新创建新的对象
- 推荐使用