单例设计模式Singleton
存在意义
- 有些对象的创建消耗时间和内存是非常大的,恰恰好这些对象在我们的应用中只需要使用 1 个,如果不能得到控制,会造成资源的浪费。例如线程池、数据库连接池,一个应用程序中,我们只需要有 1 个这样的大对象。
单例模式的两种写法:懒汉式和饿汉式
说明
- 饿汉式:在程序启动或单件模式类被加载的时候,单件模式实例就已经被创建。(线程安全)
- 懒汉式:当程序第一次访问单件模式实例时才进行创建。(线程不安全)
如何选择
如果单件模式实例在系统中经常会被用到,饿汉式是一个不错的选择。
如果单件模式在系统中会很少用到或者几乎不会用到,那么懒汉式是一个不错的选择。
懒汉式
/**
* 单例懒汉式:当需要用到的时候才会创建实例,缺点:线程不安全
*
* @title
* @description
* @since JDK1.8
*/
public class SingleTon {
private static SingleTon single;
// 构造器私有化,不能在类的外部随意创建对象
private SingleTon() {
}
//重点:同步不要加在方法上,加在可能发生线程不安全的地方(写操作而不是读操作)
public static SingleTon getSingleTon() {
if (single == null) {
// System.out.println("hello");
synchronized (SingleTon.class) {
if (single == null)
single = new SingleTon();
}
}
return single;
}
}
饿汉式
/**
* 单例模式饿汉式:class文件被加载的时候创建实例,缺点:内存消耗大
*
* @title
* @description
* @since JDK1.8
*/
public class SingleTon1 {
private static SingleTon1 single = new SingleTon1();
private SingleTon1() {
}
public static SingleTon1 getSingleTon1() {
return single;
}
}
测试类
/**
* 单例测试类
* @title
* @description
* @since JDK1.8
*/
public class Main {
public static void main(String[] args) {
// // 懒汉测试
// SingleTon single1 = SingleTon.getSingleTon();
// SingleTon single2 = SingleTon.getSingleTon();
// System.out.println(single1 == single2);
// // 饿汉测试
// SingleTon1 single3 = SingleTon1.getSingleTon1();
// SingleTon1 single4 = SingleTon1.getSingleTon1();
// System.out.println(single3 == single4);
// 多线程验证懒汉式
// for (int i = 0; i < 10; i++) {
// Runnable run = new Runnable() {
// @Override
// public void run() {
// SingleTon single5 = SingleTon.getSingleTon();
// System.out.println(single5);
// }
// };
// Thread t = new Thread(run);
// t.start();
// }
Runnable run = () -> {
//懒汉式多线程测试
SingleTon single5 = SingleTon.getSingleTon();
System.out.println(single5);
};
for (int i = 0; i < 10; i++) {
Thread t = new Thread(run);
t.start();
}
}
}
扩展
- 单例模式用途:
单例模式属于工厂模式的特例,只是它不需要输入参数并且始终返回同一对象的引用。
单例模式能够保证某一类型对象在系统中的唯一性,即某类在系统中只有一个实例。它的用途十分广泛,打个比方,我们开发了一个简单的留言板,用户的每一次留言都要将留言信息写入到数据库中,最直观的方法是没次写入都建立一个数据库的链接。这是个简单的方法,在不考虑并发的时候这也是个不错的选择。但实际上,一个网站是并发的,并且有可能是存在大量并发操作的。如果我们对每次写入都创建一个数据库连接,那么很容易的系统会出现瓶颈,系统的精力将会很多的放在维护链接上而非直接查询操作上。这显然是不可取的。
如果我们能够保证系统中自始至终只有唯一一个数据库连接对象,显然我们会节省很多内存开销和cpu利用率。