java单例模式实例_Java设计模式之单例模式 通俗易懂 超详细 【内含案例】

单例模式

什么是单例模式 ?

确保程序中一个类只能被实例化一次,实现这种功能就叫单例模式

单例模式的好处是什么 ?

方便控制对象

节省资源减少浪费

怎么实现单例模式 ?

构造私有化

调用静态方法返回实例

确保对象的实例只有一个

常见的单例模式有哪些 ?

饿汉式

把对象创建好,需要使用的时候直接用就行饥肠辘辘 非常着急

懒汉式

由于饿汉式容易浪费资源,比如类里 有public static 修饰的一个方法 test(),即可不创建实例就可访问到

懒 不到万不得已不创建实例,什么时候用什么时候创建

懒汉式的缺点呢 ?

线程不安全 但是可以控制 如何控制,下面会讲

代码实现饿汉式

创建 Singleton.java 类

public class Singleton1 {

public static final Singleton1 INSTANCE = new Singleton1();

//构造方法私有化

private Singleton1(){

}

//get方式获取对象

public static Singleton1 getInstance(){

return INSTANCE;

}

}

创建 TestSingleton.java 类

public class TestSingleton{

public static void main(String[] args) {

Singleton1 st1 = Singleton1.INSTANCE;

Singleton1 st2 = Singleton1.getInstance();

//return true

System.out.println(st1.hashCode() == st2.hashCode());

//return true

System.out.println(st1 == st2);

}

}

代码实现懒汉式

创建 Singleton2.java 类

public class Singleton2 {

private static Singleton2 INSTANCE;

//构造方法私有化

private Singleton2(){

}

//get方式获取对象

public static Singleton2 getInstance(){

if (INSTANCE == null) {

INSTANCE = new Singleton2();

}

return INSTANCE;

}

}

x修改 TestSingleton.java 类

Singleton2 a = Singleton2.getInstance();

Singleton2 b = Singleton2.getInstance();

//ture

System.out.println(a == b);

注意: 再有的情况下懒汉式线程不安全比如多线程

举例说明 线程不安全

修改 Singleton2.java 类

public static Singleton2 getInstance(){

if (INSTANCE == null) {

try {

Thread.sleep((int) Math.random() * 100);

} catch (InterruptedException e) {

e.printStackTrace();

}

INSTANCE = new Singleton2();

}

return INSTANCE;

}

修改 TestSingleton.java 类

Callable c = new Callable() {

@Override

public Singleton2 call() throws Exception {

return Singleton2.getInstance();

}

};

//创建两个线程池

ExecutorService es = Executors.newFixedThreadPool(2);

Future f1 = es.submit(c);

Future f2 = es.submit(c);

Singleton2 a = f1.get();

Singleton2 b = f2.get();

/**

* 有可能两种情况

* 1. 判断是否一致:false

* 2. 判断是否一致:true

*/

System.out.println("判断是否一致:" + (a == b));

为什么会这样呢 ?

因为第一个线程 if (INSTANCE == null) 进去之后触发 sleep() 线程休眠

第二个线程就紧跟着 if (INSTANCE == null) 这时 INSTANCE 还是等于null,随后进入线程休眠

这样他两个都创建了实例,一共创建两次所以导致线程不安全

怎么解决线程不安全 ?

解决 懒汉式 线程不安全的方法

使用 synchronized 同步锁

使用静态内部类

使用 synchronized 同步锁

修改 Singleton2.java 类

public static Singleton2 getInstance(){

//提高效率 已经new过后不需要再等着资源

if (INSTANCE == null) {

//可以使用同步锁 完成线程安全

synchronized (Singleton1.class) {

if (INSTANCE == null) {

try {

Thread.sleep((int) Math.random() * 100);

} catch (InterruptedException e) {

e.printStackTrace();

}

INSTANCE = new Singleton2();

}

}

}

return INSTANCE;

}

再次运行 TestSingleton.java 测试 就会返回 true

但是还是有一个问题,第一个线程已经创建好了,第二个线程还需要再次进入 synchronized块等待资源的控制权.可以在外面加上 if 判断 INSTANCE == null 即可提高效率

使用静态内部类

因使用 synchronized 同步锁,代码看起来不雅观,所以可以使用静态内部类,达到线程安全

修改 Singleton2.java 类

public class Singleton2 {

//构造私有化

private Singleton2(){

}

//在内部类被加载时,才创建

//静态内部类 不会随着外部类加载,初始化 它是一个独立的 当用这个类时才会加载初始化

//在内部类加载和初始化,所以线程是安全的

private static class Inner{

private static final Singleton2 INSTANCE = new Singleton2();

}

//get 方式获取静态内部类的INSTANCE

public static Singleton2 getInstance(){

return Inner.INSTANCE;

}

}

小结

并不能说明 懒汉式 要强于 饿汉式,可以根据项目需求,来使用其中一种模式

再次推荐 保你面试必过的 java面试题

本文就先说到这里,有问题欢迎留言讨论

你可能感兴趣的:(java单例模式实例)