什么是单例
简单解释 在当前内存(jvm)中某个类信息只能有一个实例存在。
原理 提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意事项
单例类只能有一个实例。
单例类必须自己创建自己的唯一实例。
单例类必须给所有其他对象提供这一实例。
简单解释 单例类的构造方法必须是私有的,只能在自己内部创建。(这样做是从根本上杜绝出现多个实例)
构造方法既然是私有的,那肯定只能在内部调用。
声明一个静态变量只给这个静态变量初始化,提供给外部调用。
优点
在内存里单个类信息只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例,还有高并发环境下频繁获取相同的对象或者属性(比如当前时间)
使用场景
系统内部创建多个对象会占用相应的资源,当前对象实例中的属性不需要或者不会变更的可以使用。
比如 操作系统中的回收站,没人会选择在操作系统中安装俩个回收站
java程序而言的话比如 学生表中有一系列的类型(性别,考试等级)这些值基本上是固定的,这些值可以通过后台返回给前台,通常会用到字典表,每次返回的对象属性信息都是一样的,就可以在项目启动初始化的时候把这些值加载到内存中,以后每次需要直接在内存中取出使用。避免重复创建对象,访问量很大的话内存占用也会少些。
代码实践
饿汉式单例
//饿汉式单例
public class Signlegton2 {
private Signlegton2() {
System.out.println("我来初始化了");
}
private static Signlegton2 signlegton = new Signlegton2();
public static Signlegton2 getSignlegton(){
return signlegton;
}
public static void main(String[] args) {
Signlegton2 signlegton1 = Signlegton2. getSignlegton();
Signlegton2 signlegton2 = Signlegton2. getSignlegton();
}
}
//代码执行结果
我来初始化了
饿汉式加载,顾名思义不管你需不需要我都给你加载出来,这种加载方式天生是线程安全的,在类被引用的时候即创建一个实例,不需要考虑多个线程同时调用带来的问题。一般情况下单例模式的初始化会选择这个方式,但是也会有一个问题,如果这个实例我们并不需要,但是此类中的其他信息会被引用,而自动加载当前实例(因为是静态的),导致资源浪费,占用内存。最终我们可以考虑静态内部类的调用方式,可以防止这种情况。我们继续往下面看
懒汉式单例
//实现方式 懒汉式单例
public class Signlegton {
private Signlegton() {
System.out.println("我来初始化了");
}
private static Signlegton signlegton;
public static Signlegton getSignlegton(){
if (signlegton == null){
signlegton = new Signlegton();
}
return signlegton;
}
public static void main(String[] args) {
Signlegton signlegton1 = Signlegton. getSignlegton();
Signlegton signlegton2 = Signlegton. getSignlegton();
}
}
//代码执行结果
我来初始化了
顾名思义,懒汉式单例,也就是是说我不会自己实例化,你过来第一次调用才会初始化实例。这样调好处是如果当前对象没有地方使用,内存会先不占用。但是高并发调场景下就不是那样了,多个线程同时调用的情境下初始化可能会被同时间调用多次,并不能保证就是单例。
双重锁机制
//懒汉式单例 多个线程初始化时解决方案 双重锁机制
public class Signlegton1 {
private Signlegton1() {
System.out.println("我来初始化了");
}
private static Signlegton1 signlegton;
public static Signlegton1 getSignlegton(){
if (signlegton == null){
synchronized (Signlegton1.class){
if (signlegton == null){
signlegton = new Signlegton1();
}
}
}
return signlegton;
}
public static void main(String[] args) {
Signlegton1 signlegton1 = Signlegton1. getSignlegton();
Signlegton1 signlegton2 = Signlegton1. getSignlegton();
}
}
//代码执行结果
我来初始化了
加锁,保证当前实例在创建时只能被一个线程使用,这样也就保证了任何环境下都只能有一个实例被创建在内存中。不过还是有问题,初始化的时候因为一些代码逻辑导致线程堵塞,后面多个线程等待,本身单例模式是为了提升效率的这样一整反而效率下降了。
静态内部类的方式
//静态内部类的方式
public class Signlegton3 {
private Signlegton3() {
System.out.println("我来初始化了");
}
public static class InitSignlegton3{
public static final Signlegton3 signlegton = new Signlegton3();
}
public static void main(String[] args) {
Signlegton3 signlegton1 = InitSignlegton3.signlegton;
Signlegton3 signlegton2 = InitSignlegton3.signlegton;
}
}
//代码执行结果
我来初始化了
推荐使用此方法用来创建单例模式,在引用当前对象的时候并不会直接创建无需使用的对象实例,还能保证线程安全。当然软件肯定是有bug出现的,这样可能还是有问题,万一初始化失败了呢。一定要抱着敬畏之心去看待代码,烦躁的时候要静下心来先去做一些其他的事情。
希望本文可以帮到你。