首页 > 编程语言 >java 单例模式

java 单例模式

时间:2024-04-14 14:45:42浏览次数:30  
标签:java SingletonJDBC 模式 instance static private 单例 public

单例模式(Singleton Pattern)

  是 Java 中最简单的设计模式之一,这种类型的设计模式属于创建型模式。目的是确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。这样做可以节省系统资源,并且保证某些类在系统中只存在一个实例。

  主要解决:一个全局使用的类频繁地创建与销毁。

  如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

  关键代码:构造函数是私有的(private关键字)

  缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

  注意:

    1、单例类只能有一个实例。

    2、单例类必须自己创建自己的唯一实例。

    3、单例类必须给所有其他对象提供这一实例。

  类型:单例模式可以分为几种类型,包括 饿汉式,懒汉式,登记式 。饿汉式单例在类加载时就创建实例,而懒汉式单例则在首次使用时创建实例。这两种实现方式都需要考虑线程安全问题。

  多线程情况下: 双重检查锁定(double-checked locking)是一种常用的实现线程安全单例的方法。

======================================================== 以上八股文 来源  网上乱查的 ================================================================================================

举个简单小例子:  在使用数据库时, 首先要获取 jdbc 链接,  然后进行增删改查操作,  每次 增加操作   ,删除操作 ,查询 和修改操作时, 都要获取  jdbc 链接。

          那么这个时候, 只保存一个 jdbc链接 在系统中, 每次操作数据库时 使用创建好 的JDBC 链接 就不需要每次操作 都创建了

代码:

/**
 * 饿汉式
 */
public class SingletonJDBC {
    private static SingletonJDBC instance = new SingletonJDBC();
    private SingletonJDBC(){}
    public static SingletonJDBC getInstance(){
        return instance;
    }
    public void JdbcMessage(){
        System.out.println(" JDBC://XXXXXXXXXXXXXXXXXXXX  ");
    }
}
public static void main(String[] args) {
  //SingletonJDBC jdbc = new SingletonJDBC();
SingletonJDBC singletonJDBC = SingletonJDBC.getInstance();
singletonJDBC.JdbcMessage();
}
 

标红地方会报如下错误,private

 

以上代码 使用了static 关键字(static 当Java虚拟机(JVM)加载类时,就会执行该代码块),  加载的时候就将 jdbc 给 创建好了, 可是, 在这个时候不操作数据库,那么就不应该 加载, 在使用的时候再加载(懒汉式出现 lazy loading)

代码:

/**
 * 懒汉汉式 lazy loading
 */
public class SingletonJDBC {
    private static SingletonJDBC instance;
    private SingletonJDBC(){}
    public static SingletonJDBC getInstance(){
        if (null == instance){
            instance = new SingletonJDBC();
        }
        return instance;
    }
    public void JdbcMessage(){
        System.out.println(" JDBC://XXXXXXXXXXXXXXXXXXXX  ");
    }
}

出现问题了,  在使用数据库的时候,多个地方 同时(多线程) 需要jdbc 链接 ,那么当第一个 使用者 来的时候, 走 到了 null == instance 的时候, 第二个使用者来了, 也是空, 那么 它们创建的 就不是一个 相同的 SingletonJDBC 了 !!

验证一下

private static SingletonJDBC instance;
    private SingletonJDBC(){}
    public static SingletonJDBC getInstance(){
        if (null == instance){
            try {
                Thread.sleep(10);
            }catch (Exception e){
                e.printStackTrace();
            }
            instance = new SingletonJDBC();
        }
        return instance;
    }
    public void JdbcMessage(){
        System.out.println(" JDBC://XXXXXXXXXXXXXXXXXXXX  ");
    }
}
public static void main(String[] args) {
   for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                SingletonJDBC singletonJDBC = SingletonJDBC.getInstance();
                singletonJDBC.JdbcMessage();
                System.out.println(singletonJDBC.hashCode());
            }).start();
}
}

结果:

这些  hashCode 不对, 不是一个 (又增加了多余的开销,如果是业务中的唯一数据, 这不就出问题了么)

 

修改一下   加个 synchronized,代码如下

    public static synchronized SingletonJDBC getInstance(){
        if (null == instance){
            try {
                Thread.sleep(10);
            }catch (Exception e){
                e.printStackTrace();
            }
            instance = new SingletonJDBC();
        }
        return instance;
    }

结果:完美解决问题  (但是, 加了synchronized 是锁住了 SingletonJDBC 整个 对象, 每次过来 要判断 锁 的情况, 效率又低了【如果是上千万数据交换】)

 

 修改一下   (双重检查),代码如下   ( volatile 关键字 ) 

public class SingletonJDBC {
private static volatile SingletonJDBC instance;
private SingletonJDBC() {
}
public static SingletonJDBC getInstance() {
if (null == instance) {
synchronized (SingletonJDBC.class) {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
if (null == instance) {
instance = new SingletonJDBC();
}
}
}
return instance;
}

public void JdbcMessage() {
System.out.println(" JDBC://XXXXXXXXXXXXXXXXXXXX ");
}
}

 

代码 复杂,效果不是很理想,修改一下, (登记式/静态内部类)

/**
 * 登记式/静态内部类
 */
public class SingletonJDBC {
    private SingletonJDBC() {
    }
    private static class SingletonJDBCHolder {
        private static final SingletonJDBC INSTANCE = new SingletonJDBC();
    }
    public static SingletonJDBC getInstance() {
        return SingletonJDBCHolder.INSTANCE;
    }
    public void JdbcMessage() {
        System.out.println(" JDBC://XXXXXXXXXXXXXXXXXXXX  ");
    }
}

说明: 在类加载 时候, 是不会  加载静态内部类的 , 只有当调用 getInstance 方法时候,会显式装载   SingletonJDBCHolder 。这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程。

 

结合上篇文章 , (工厂模式 + 单例模式) 可以简单的设计一个  数据库链接复用代码 

=============================================================== 收    工====================================================================

 

 

补充 以下内容来源     ==============================菜鸟教程=================================

枚举

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。

实例

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

经验之谈:一般情况下,不建议使用 懒汉方式,建议使用  (登记式/静态内部类),如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用 双检锁方式。

 

标签:java,SingletonJDBC,模式,instance,static,private,单例,public
From: https://www.cnblogs.com/zy115/p/18134001

相关文章

  • java中的包装类
    java中的包装类为什么要有包装类在面向对象中“一切皆对象”,但是基本类型的数据不太符合这一理念,为了统一概念,因此,把基本类型包装成了引用类型的数据基本类型对象的包装类型,除了可以满足基本的类型的基本需求,还附加了其他的方法,例如:类型转化,数据类型之间的操作等等基本类型......
  • [javascript]知识点
    【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)https://www.cnblogs.com/cnb-yuchen/p/18031963出自【进步*于辰的博客】目录1、其他知识点链接2、严格模式3、类、对象3.1介绍3.2自定义对象3.2.1创建对象方法3.2.2建立继承关系4、变量5、this10、关于var与......
  • Java集合
    Java集合本文中引入的源码为JDK11什么是Java集合Java集合(Java集合类)是java数据结构的实现。Java集合类是java.util包中重要内容,它允许以各种方式将元素分组,并定义了各种使这些元素更容易操作的方法。Java集合类是Java将一些基本的和使用频率极高的基础类进行封装和增强后再......
  • ES7.17.20连接时报错:java.lang.NoSuchMethodError: org.elasticsearch.client.Request
    1.报错详情:java.lang.NoSuchMethodError:org.elasticsearch.client.RequestOptions$Builder.removeHeader(Ljava/lang/String;)Lorg/elasticsearch/client/RequestOptions$Builder; atco.elastic.clients.transport.rest_client.RestClientOptions.addBuiltinHeaders(RestCli......
  • java: 无法访问org.mybatis.spring.annotation.MapperScan、类文件具有错误的版本 61.
    使用的Mybatis-spring依赖的版本3.0.1太高,将版本改为2.2.2<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><depende......
  • 使用java client连接ES7.17.20时报错:Caused by: java.lang.NoClassDefFoundError: Cou
    1.报错详情Causedby:java.lang.NoClassDefFoundError:Couldnotinitializeclasscom.fasterxml.jackson.databind.cfg.DatatypeFeatures$DefaultHolder atcom.fasterxml.jackson.databind.cfg.DatatypeFeatures.defaultFeatures(DatatypeFeatures.java:33)~[jackson-data......
  • JavaScript基础
    JavaScript一、js编写位置位置1:script标签中<scripttype="text/javascript"> vara=1;...</script>位置2:标签中绑定事件<buttononlick="alert(123);">Click</button>位置3:超链接的href属性中<ahref="javascript:alert('......
  • 读所罗门的密码笔记19_治理模式
    1. 解决方案1.1. 全球人工智能的环境错综复杂,它严重依赖于价值观,且关系重大1.2. 即使是与大家同仇敌忾的问题做斗争,也往往无法在国际社会中取得最佳效果1.3. OPCW(禁止化学武器组织)已经帮助限制了化学武器的开发和部署,但没有协议是百分百奏效的1.4. 如果《核不扩散条约》......
  • Java中的matches()方法与find()方法区别
    正则表达式用于在字符串中进行模式匹配。在处理字符串时,经常需要使用matches()方法和find()方法来查找与正则表达式匹配的部分。虽然它们都用于匹配字符串,但有一些重要的区别。matches()方法matches()方法是String类中的一个方法,用于判断整个字符串是否与给定的正则表达......
  • 大公司的Java面试题集
    找工作要面试,有面试就有对付面试的办法。以下一些题目来自我和我朋友痛苦的面试经历,提这些问题的公司包括IBM,E*Trade,Siebel,Motorola,SUN, 以及其它大小公司。面试是没什么道理可讲的,它的题目有的不合情理、脱离实际。有在纸上写的,有当面考你的,也有在电话里问的,给你IDE的......