定义
定义:顾名思义,单例(单个实例),即一个类中只能有一个实例(对象),使得类的一个对象成为系统中的唯一实例
分类
1.懒汉式:在类加载时,不创建实例,所以类加载速度快,但运行时获取对象的速度慢
2.饿汉式:在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
代码实现(java)
饿汉式
- 饿汉式(线程安全)
class Singleton{
// 构造方法私有化
private Singleton() {
}
// 创建对象
private static Singleton instance = new Singleton();
// 提供可供获取对象的方法
public static Singleton getInstance() {
return instance;
}
}
public class SingletonDemo01 {
public static void main(String[] args) {
Singleton s1= Singleton.getInstance();
Singleton s2= Singleton.getInstance();
System.out.println(s1 == s2); // true
}
}
注:之后的main()方法测试都省略…
懒汉式
- 懒汉式(线程不安全),下列代码将通过多线程操作演示
public class SingletonDemo {
public static void main(String[] args) {
ExecutorService threadpool = Executors.newFixedThreadPool(20);
for(int i = 0 ; i <100 ; i++) {
threadpool.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+":"+Singleton.getInstance().hashCode());
}
});
}
}
}
class Singleton{
// 构造方法私有化
private Singleton() {
}
// 创建对象
private static Singleton instance = null;
// 提供可供获取对象的方法
public static Singleton getInstance() {
try {
Thread.sleep(1000); // 模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
if (instance == null) { // ①
// 创建对象
instance = new Singleton(); // ②
}
return instance;
}
}
思考:观察上述代码,假如多线程并发操作,真的满足一个类中只创建一个实例的方法吗?
->假设多线程同时操作调用getInstance()方法,thread-1线程此时判断instance == null成立时,将要向下执行创建对象操作时可能会遭到阻塞造成此时进入就绪态。
thread-2线程判断instance == null成立,开始进入②创建对象。而此时的thread-2线程也可能从就绪态到运行态,开始执行②。此时两个线程返回的是两个不同的对象,违反了单例模式的定义,怎样解决呢?
- 懒汉式(优化成线程安全的单例模式,使用同步方法)
class Singleton{
// 构造方法私有化
private Singleton() {
}
// 创建对象
private static Singletoninstance = null;
// 提供可供获取对象的方法
// 添加synchronized变成同步方法
public static synchronized Singleton getInstance() {
if (instance == null) { ①
// 创建对象
instance = new Person(); ②
}
return instance;
}
}
思考:加锁synchronized可以保证单例,但是效率高吗?多线程情况下每次一个执行完这个同步方法后另外一个线程才能继续执行,此时的效率性能是极低的。
其实只要能创建第一个对象时才需要加锁,后面的线程就不需要了。如何改进呢?
- 懒汉式(优化成线程安全的单例模式,使用双重校验锁)
// 优化:使用同步代码块且在之前加入判空大大增加了效率
class Singleton{
// 构造方法私有化
private Singleton() {
}
// 创建对象
private static Singleton instance = null;
// 提供可供获取对象的方法
// 添加synchronized变成同步方法
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
// 创建对象
instance = new Singleton();
}
}
}
return instance;
}
}
优缺点和应用场景
1.缺点:没有抽象层(接口),难于扩展…
2.优点:不需要频繁的创建对象,减少内存的消耗…
3.使用场景:数据库连接池,配置文件的读取,web中的计数器等