package net.csdn.se.singletondemo;
/**
* 线程安全的---懒汉式(懒加载)创建
*/
public class Singleton06 {
//1\
private Singleton06(){}
/**
* 2静态的内部类的方式来创建实例
* 在内部类被加载和初始化时,才创建实例。
* 静态内部类不会随着外部类的加载和初始化而初始化,而是在需要的时候才会进行加载和初始化
*/
private static class Inner{
private static Singleton06 INSTANCE = new Singleton06();
}
//3
public static Singleton06 getInstance(){
return Inner.INSTANCE;
}
public static void main(String[] args) {
Singleton06 instance = Singleton06.getInstance();
Singleton06 instance1 = Singleton06.getInstance();
System.out.println(instance == instance1);//结果一直为True
}
}
- 饿汉式创建三种方式
package net.csdn.se.singletondemo; /** * 饿汉式直接创建单例 * 1、只能创建一个实例 ---> 构造方法私有化 * 2、创建出来的实例进行保存 * 3、对外暴露 * 总结: * 1、优点:基于classloader机制来创建的单例,没有不加锁执行效率高、但是也避免了线程安全问题 * 2、缺点:类加载时即初始化了实例,因此没有达到lazyloading(延迟加载)的效果对于那些创建出来没有被使用的也没有被回收的类来说,导致浪费内存空间 */ public class Singleton01 { //1. 第一步,构造方法私有化,只创建一个实例 private Singleton01(){} //2. 第二步,对创建出来的实例进行保存 private static Singleton01 INSTANCE = new Singleton01(); //3、第三步 对外进行暴露 public Singleton01 getInstance(){ return INSTANCE; } }
package net.csdn.se.singletondemo; /** * 饿汉式 枚举的方式创建单例 * 特点: * 代码简单,实现容易 */ public enum Singleton02 { INSTANCE; } class Singleton02Test{ public static void main(String[] args) { Singleton02 instance = Singleton02.INSTANCE; } }
package net.csdn.se.singletondemo; import javax.sound.midi.Soundbank; import java.io.IOException; import java.util.Properties; /** * 饿汉式实例 * 使用静态代码块的方式创建单例 */ public class Singleton03 { //2\ 第二要素 进行保存 private static Singleton03 INSTANCE; // static { INSTANCE = new Singleton03(); //类加载的时候 可以写业务逻辑 //加载配置文件 Properties properties = new Properties(); try { properties.load(Singleton03.class.getClassLoader().getResourceAsStream("jdbc.properties")); String username = properties.getProperty("userName"); String password = properties.getProperty("password"); System.out.println(username); System.out.println(password); } catch (IOException e) { e.printStackTrace(); } } //1 第一要素 构造方法私有化 private Singleton03(){} //3 对外暴露 public static Singleton03 getInstance(){ return INSTANCE; } public static void main(String[] args) { Singleton03 instance = Singleton03.getInstance(); Singleton03 instance2 = Singleton03.getInstance(); System.out.println(instance == instance2); } }
- 懒汉式创建三种方式
package net.csdn.se.singletondemo; import java.awt.*; /** * 线程不安全的方式创建单例---懒汉式 * 线程安全的方式创建单例 添加synchronized----懒汉式 * 优点:layloading,第一次调用的使用才初始化,避免了内存的浪费 * 缺点:必须加锁才能保证是单例的,影响了效率 */ public class Singleton04 { private static Singleton04 INSTANCE; private Singleton04(){} // public static Singleton04 getInstance(){ 线程不安全创建方式 public static synchronized Singleton04 getInstance(){ //线程安全创建方式 if(INSTANCE == null){ // INSTANCE = new Singleton04(); } return INSTANCE; } }
package net.csdn.se.singletondemo;
/**
* 线程安全的---懒汉式(懒加载)创建
* 静态内部类不能传递参数
*/
public class Singleton06 {
//1\
private Singleton06(){}
/**
* 2静态的内部类的方式来创建实例
* 在内部类被加载和初始化时,才创建实例。
* 静态内部类不会随着外部类的加载和初始化而初始化,而是在需要的时候才会进行加载和初始化
/**
* 2静态的内部类的方式来创建实例
静态内部类的优点是:外部类加载时并不需要立即加载内部类,
内部类不被加载则不去初始化INSTANCE,故而不占内存。
即当SingleTon第一次被加载时,并不需要去加载SingleTonHoler,只有当getInstance()方法第一次被调用时,
才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,
这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
那么,静态内部类又是如何实现线程安全的呢?
getInstance()方法,Inner.INSTANCE,取的是Inner里的INSTANCE对象,
跟上面那个DCL方法不同的是,getInstance()方法并没有多次去new对象,
故不管多少个线程去调用getInstance()方法,取的都是同一个INSTANCE对象,
而不用去重新创建。当getInstance()方法被调用时,Inner才在Singleton06的运行时常量池里,
把符号引用替换为直接引用,这时静态对象INSTANCE也真正被创建,然后再被getInstance()方法返回出去,
这点同饿汉模式。那么INSTANCE在创建过程中又是如何保证线程安全的呢?在《深入理解JAVA虚拟机》中,有这么一句话:
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。
故而,可以看出INSTANCE在创建过程中是线程安全的,所以说静态内部类形式的单例可保证线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
*/
*/
private static class Inner{
private static Singleton06 INSTANCE = new Singleton06();
}
//3
public static Singleton06 getInstance(){
return Inner.INSTANCE;
}
public static void main(String[] args) {
Singleton06 instance = Singleton06.getInstance();
Singleton06 instance1 = Singleton06.getInstance();
System.out.println(instance == instance1);//结果一直为True
}
}