单例模式
单例模式
使用场景:
1.要求生产唯一序列号
2.WEB中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来
3.创建的一个对象需要消耗的资源过多,比如I/O与数据库的连接
单例模式的应用场景 :
网站的计数器、应用程序的日志应用、数据路连接池(这个池子)、项目读取配置文件的类、Appliction(应用程序)、windows的任务管理器、回收站
单例模式的实现
public class SingleObject {
private static SingleObject instance = new SingleObject();
private SingleObject() {
}
public static SingleObject getInstance() {
return instance;
}
public void showMessage() {
System.out.println("Hello World");
}
}
public class SingletonPattenDemo {
public static void main(String[] args) {
SingleObject object = SingleObject.getInstance();
object.showMessage();
}
}
单例模式的懒汉式实现
描述:线程不安全的,延迟对象的创建,不支持多线程,严格意义上并不是单例模式
public class SingleObject {
public static void main(String[] args) {
Bank bank1=Bank.getInstance();
Bank bank2=Bank.getInstance();
//是同一个对象
System.out.println(bank1 == bank2);
}
}
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.声明当前类对象,没有初始化
private static Bank instance=null;
//3.声明public,静态的返回当前类的对象的方法
public static Bank getInstance(){
if(instance==null){
instance=new Bank();
}
return instance;
}
}
单例模式的懒汉式实现(线程安全)
描述:加了synchronized锁,保证单例,可以在多线程中很好的工作,但是加锁会影响效率
public class SingleObject {
public static void main(String[] args) {
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
//是同一个对象
System.out.println(bank1 == bank2);
}
}
class Bank {
private static Bank instance;
private Bank() {
}
public static synchronized Bank getInstance() {
if (instance == null)
instance = new Bank();
return instance;
}
}
单例模式的饿汉式实现
描述:最常见的实现方法,线程安全的,但对象加载时间过长,容易产生垃圾对象
public class SingleObject {
public static void main(String[] args) {
Bank bank1=Bank.getInstance();
Bank bank2=Bank.getInstance();
//是同一个对象
System.out.println(bank1 == bank2);
}
}
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象,要求此对象也是静态的
private static Bank instance= new Bank();
//3.提供公共的静态方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
双重校验锁的单例模式(double-checked locking DCL)
描述:双锁机制,是线程安全的,保证了高性能。
volatile关键字:保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
Instance 采⽤ volatile 关键字修饰也是很有必要的
Instance = new Singleton();
这段代码其实是分为三步执⾏:
1.为 Instance 分配内存空间
2.初始化 Instance
3.将 Instance 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成132。指令重排在单线程环境下不会出 现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。例如,线程 T1 执⾏了 1 和 3,此时 T2 调⽤ getInstance() 后发现 Instance 不为空,因此返回 Instance,但此时 Instance 还未被初始化。 使⽤ volatile 可以禁⽌ JVM 的指令重排,保证在多线程环境下也能正常运⾏。
public class Singleton{
private Singleton(){}
public volatile static Singleton instance; //volatile不能少
public static Singleton getInstace(){
if(instance == null){ //single checked
synchronized(Singleton.class){
if(instance == null){ //double checked
instance = new Singleton();
}
}
return instance;
}
}
}
选择题
【单选】以下哪段代码,调用 getHelper 的过程,不是线程安全的
A.
public class LazyInitDemo {
private static Helper HELPER = null;
public static synchronized Helper getHelper() {
if (HELPER == null) {
HELPER = new Helper();
}
return HELPER;
}
}
B.
public class LazyInitDemo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized (this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
}
C.
public class LazyInitDemo {
private static class HelperHolder {
private static final Helper HELPER = new Helper();
}
public static final Helper getHelper() {
return HelperHolder.HELPER;
}
}
D.
public class PreInitDemo{
private static final Helper HELPER = new Helper();
public static Helper getHelper(){
return HELPER;
}
}
A,单例模式的懒汉式实现,并且是线程安全的
B,没有volatile关键字,不是线程安全的
C,静态内部类实现单例模式,是线程安全的
(这里参考:https://blog.csdn.net/qq_35590091/article/details/107348114?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1.no_search_link&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1.no_search_link)
D,单例模式的饿汉式实现,是线程安全的