目录
- 1.什么是单例模型
- 2.单例模式的写法
- 2.1饿汉模式
- 2.2懒汉模式
- 2.3饿汉模式和懒汉模式的比较
- 2.4懒汉模式的改进
- 3.小结
1.什么是单例模型
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
单例 =>单个实例(instance) 对象
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。一个程序中只能创建一个实例(对象),不能创建多个对象,java的单例模式借助语法,保证某个类只能创建一个对象,不能new多个对象,让所有需要调用的地方都共享这一单例对象。
同时单例模式也是我们校招中最常考的模式之一
2.单例模式的写法
单例模式的写法有很多,大约5,6种,但是我们这里只讲述两种方法饿汉模式和懒汉模式
给大家举个例子:
比如我们要打开硬盘上的文件时.读取文件内容是
饿汉模式:直接就把文件上内容全读取到内存上
懒汉模式:读取文件一小部分,到当前屏幕上,如果用户翻页了就读取其他内容,不翻页就省下来了
当我们的文件很大时 10G,饿汉模式不知道内存够不够,可能卡了半天打不开
但是我们的懒汉模式很快就能打开了
下面我们来细致讲讲两种模式
2.1饿汉模式
private static Singletn instace =new Singletn();
此时instance就是唯一的实例,因为在类中,只有一个对象,而类成员变量也是唯一的
//把这个例设成单例
//饿汉模式
//只是读操作
class Singletn {
//static 修饰,是类属性,类直接调用
//JVM中每个类对象只有一份,类对象里面的成员自然也是一份
private static Singletn instace =new Singletn();
//获取实例方法
public static Singletn getInstance()
{
return instace;
}
//设置成私有类,外部类不能使用
private Singletn(){
}
}
在我们的main中就不能new一个新的对象了,因为我们在上面的类中private修饰的对象
public class ThreadDemo10 {
public static void main(String[] args) {
//s1和s2是同一个对象
Singletn s1= Singletn.getInstance();
Singletn s2= Singletn.getInstance();
//Singletn s3=new Singletn(); //错误的不能new
}
}
同时再类内部把实例创建好,在外部不能创建实例就可以吃保证单例的特性了
2.2懒汉模式
什么是懒汉模式?
就是类加载的时候不创建实例. 第一次使用的时候才创建实例
比如下面这个代码,先创建一个类对象,但是为空,在第一次使用的时候才会创建
//懒汉单例模式
//有读操作有写操作
class Singletnlazy {
volatile private static Singletnlazy instance=null;
//volatile 保存内存可见性
//保存 禁止指令重排序
public static Singletnlazy getInstance()
{
if (instance == null)
{
instance = new Singletnlazy();
}
return instance;
}
private Singletnlazy()
{
}
}
2.3饿汉模式和懒汉模式的比较
大家看这两个代码,左边的是饿汉模式的,右边的是懒汉模式的
可以看出,饿汉模式只有读操作,而懒汉模式不仅有读操作还有修改操作
这就导致了饿汉模式是安全的,但是懒汉模式的多线程是不安全的
2.4懒汉模式的改进
这种情况下就会导致多new了一个对象(这个问题就很严重了)
原因在于if操作不是原子操作
这个时候我们就要变为原子操作,我们使用synchronized来让if语句和new语句一起变为原子操作
public static Singletnlazy getInstance()
{
//保证if和new是一个原子操作
synchronized (Singletnlazy.class)
{
if (instance == null)
{
instance = new Singletnlazy();
}
}
return instance;
}
但是频繁的加锁是很麻烦的,是低效的操作(可能会导致阻塞等待)
那我们就应该让降低加锁频率我们使用双重if语句来降低锁竞争频率,只在还没创建的时候才会给加锁
public static Singletnlazy getInstance()
{
//使用双重 if 判定, 降低锁竞争的频率.
if(instance==null)
{
//保证if和new是一个原子操作
synchronized (Singletnlazy.class)
{
if (instance == null)
{
instance = new Singletnlazy();
}
}
}
return instance;
}
虽然两个if语句一样,但是作用确实差别很大的,执行时间也是天壤之别
这个时候我们的代码又出现了新的问题,指令重排序
例如原本我们的顺序是1,2,3
但是发生了指令重排序
就会变为1,3,2
虽然指令重排序的概率很小但是我们为了以防万一,我们还是要进行改进的,在创建对象的时候加一个volatile
class Singletnlazy {
volatile private static Singletnlazy instance=null;
//volatile 保存内存可见性
//保存 禁止指令重排序
public static Singletnlazy getInstance()
{
//使用双重 if 判定, 降低锁竞争的频率.
if(instance==null)
{
//保证if和new是一个原子操作
synchronized (Singletnlazy.class)
{
if (instance == null)
{
instance = new Singletnlazy();
}
}
}
return instance;
}
}
3.小结
单例模式,线程安全问题
饿汉模式:线程是安全的,只进行读操作
懒汉模式:不安全,有读操作也有写操作
为此我们进行以下操作改进懒汉模式