企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] ## 1. 单例模式 **饿汉**由jvm保证单例性:类属性只有在类加载的时候保证一次初始化(**编译时,将类方法收集到clinit<>方法中,clinit<>方法保证百分之百的同步,满足可见、原子、顺序性**) **懒汉:**需要收到加synchronized进行同步 ### 1.1 饿汉模式 > * 饿汉模式 优点是:写起来比较简单,而且不存在多线程同步问题,避免了synchronized所造成的性能问题; 缺点是:当类SingletonTest被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。 ~~~ /** * 方法一 * 单例模式的实现:饿汉式,线程安全 但效率比较低 */ public class SingletonTest { // 1. 定义一个私有的构造方法 private SingletonTest() { } // 2. 将自身的实例对象设置为一个属性,并加上Static和final修饰符 private static final SingletonTest instance = new SingletonTest(); // 3. 静态方法返回该类的实例 public static SingletonTest getInstancei() { return instance; } } ~~~ * * * * * ### 1.2 饱汉模式 ~~~ /** * * 单例模式的实现:饱汉式,线程安全简单实现 * */ public class SingletonTest { // 定义一个私有构造方法 private SingletonTest() { } //定义一个静态私有变量(不初始化,不使用final关键字,使用volatile保证了多线程访问时instance变量的可见性,避免了instance初始化时其他变量属性还没赋值完时,被另外线程调用) private static volatile SingletonTest instance; //定义一个共有的静态方法,返回该类型实例 public static SingletonTest getIstance() { // 对象实例化时与否判断(不使用同步代码块,instance不等于null时,直接返回对象,提高运行效率) if (instance == null) { //同步代码块(对象未初始化时,使用同步代码块,保证多线程访问时对象在第一次创建后,不再重复被创建) synchronized (SingletonTest.class) { //未初始化,则初始instance变量 if (instance == null) { instance = new SingletonTest(); } } } return instance; } } ~~~ ### 1.3 静态内部类创建单例对象(推荐) 通过将这个单实例的引用变量定义在静态内部类中,来实现单例,这样可以做到不用if条件进行判断,并且是多线程安全的(由jvm保证)。 1.类加载的最有初始化过程会调用类对象<clinit>构造方法 2. <clinit>**静态变量、常量的赋值动作和静态代码块** 组成的 3. jvm保证多线程下同步加锁clinit方法,保障方法执行一次 静态内部类在用到时,加载到内存,到了初始化阶段执行clinit方法(仅一次,保证单例),执行其中的静态属性赋值(工具类初始化) ~~~ package JavaTest.SingletonTest; /** * Created by dailin on 2017/11/23. */ public class SingleTest { //定义一个内部类 private static class NestClass { private static SingleTest instance; //利用静态代码块,对外围类初始化 static { System.out.println("静态内部类NestClass静态代码块执行。。。。。"); System.out.println("instance = new SingletonTest()。。。。。。"); instance = new SingleTest(); } } // 不能直接new private SingleTest() { System.out.println("private SingletonTest()"); } public static SingleTest getInstance() { System.out.println("SingletonTest getInstance()"); return NestClass.instance; } public static void main(String[] args) { SingleTest instance = SingleTest.getInstance(); System.out.println("========================================"); SingleTest instance01 = SingleTest.getInstance(); System.out.println("========================================"); SingleTest instance02 = SingleTest.getInstance(); System.out.println(instance01 == instance); } } ~~~ 例2:通过内部类实现 KeyStore 对象的单例 ~~~ package cn.com.bigssl.crypto; import com.aexit.motordriver.commons.utils.TimingDBConnection; import java.io.FileInputStream; import java.io.InputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.Enumeration; public class TimingCertificate { private PrivateKey privateKey; private String SerialNumber; // 直接Nested.keyStore使用 static class Nested{ static KeyStore keyStore = null; static String passwd = "1"; static { String osType = System.getProperty("os.name"); String path = System.getProperty("user.home") + "/application/jilinax.pfx"; try { keyStore = KeyStore.getInstance("PKCS12"); InputStream input = new FileInputStream(path); keyStore.load(input, passwd.toCharArray()); } catch (Exception e) { e.printStackTrace(); } } } public void readPfx() throws Exception { Enumeration<String> aliases = Nested.keyStore.aliases(); if (!aliases.hasMoreElements()) throw new RuntimeException("no alias found"); String alias = aliases.nextElement(); X509Certificate cert = (X509Certificate) Nested.keyStore.getCertificate(alias); SerialNumber = cert.getSerialNumber().toString();//序列号 SerialNumber = Long.toHexString(Long.parseLong(SerialNumber)).toUpperCase(); privateKey = (PrivateKey) Nested.keyStore.getKey(alias, Nested.passwd.toCharArray());//私钥 } public PrivateKey getPrivateKey() { return privateKey; } public String getSerialNumber() { return SerialNumber; } public static void main(String[] args){ System.out.println(System.getProperty("user.home")); } } ~~~ 例3:测试单例 ~~~ package com.aixin.tuna.fdfs; import org.csource.fastdfs.*; import java.sql.Connection; import java.sql.SQLException; /** * Created by dailin on 2018/7/11. */ public class FdfsUtil { static class Nested { private static TrackerServer trackerServer =null; private static StorageServer storageServer = null; private static StorageClient storageClient = null; static { try { ClientGlobal.init("fdfs_client.conf"); TrackerClient tracker = new TrackerClient(); trackerServer = tracker.getConnection(); storageClient = new StorageClient(trackerServer, storageServer); }catch (Exception e) { e.printStackTrace(); } } } //获取单例 public static StorageClient getStorageClient() { return Nested.storageClient; } } ~~~ ~~~ import com.aixin.tuna.fdfs.FdfsUtil; import org.csource.fastdfs.StorageClient; import org.junit.Test; import javax.sound.midi.Soundbank; /** * Created by dailin on 2018/7/11. */ public class SingleInstanceTest { @Test public void singleInstanceTest() { StorageClient storageClient; StorageClient storageClient1; storageClient = FdfsUtil.getStorageClient(); storageClient1 = FdfsUtil.getStorageClient(); System.out.println(storageClient == storageClient1); int storageClientAddr = System.identityHashCode(storageClient); int storageClientAddr1 = System.identityHashCode(storageClient1); System.out.println("storageClient地址:" + storageClientAddr); System.out.println("storageClient1地址:" + storageClientAddr1); } } ~~~ ~~~ true storageClient地址:1347137144 storageClient1地址:1347137144 ~~~ ### 1.4 使用场景 适用场景: 1.需要生成唯一序列的环境 2.需要频繁实例化然后销毁的对象。 3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 4.方便资源相互通信的环境