1.定义
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式,单例模式又可以分为懒汉单例模式和恶汉单例模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
单例模式有 3 个特点:
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点;
2.单例模式的结构与实现
单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。
3.实现
3.1懒汉模式
需要的时候才创建
public class Singleton {
private static Singleton singleton=null;
public static synchronized Singleton install(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
public void SayHello(){
System.out.println("创建成功");
}
}
3.2恶汉模式
预先创建好,可以避免加锁造成的资源浪费
public class Singleton {
private static Singleton singleton=new Singleton();
public static Singleton install(){
return singleton;
}
public void SayHello(){
System.out.println("创建成功");
}
}
3.3双重加锁模式
加锁的懒汉模式看起来即解决了线程并发问题,又实现了延迟加载,然而它存在着性能问题,依然不够完美。synchronized修饰的同步方法比一般方法要慢很多,如果多次调用getInstance(),累积的性能损耗就比较大了。因此就有了双重校验锁,如下所示:
public class Singleton {
private static volatile Singleton singleton=null;
public static Singleton install(){
if(singleton==null){
synchronized (Singleton.class){
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
public void SayHello(){
System.out.println("创建成功");
}
}
Volatile关键字解决了由于指令重排优化的存在,导致初始化Singleton和将对象地址赋给instance字段的顺序是不确定的。在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错的问题。volatile的一个语义是禁止指令重排序优化,也就保证了instance变量被赋值的时候对象已经是初始化过的,从而避免了上面说到的问题。
4.多例模式
多例模式可以算作是单例模式的扩展,多例模式是说类的实例有多个,但是又不是无限多个,比如Java中的线程池,指定线程池的大小就是指定了可运行实例的大小。如下代码简单的展示了多例模式的简单使用:
public class Test {
List<Person> personList=new ArrayList<>();
int personNumber;
int index=0;
Test(int personNumber){
this.personNumber=personNumber;
}
public static Test init(int personNumber){
Test test=new Test(personNumber);
return test;
}
public Person getPerson(){
if(index>=personNumber){
index=index%personNumber;
}
if(personList.size()<personNumber){
Person person=new Person();
person.setName(""+index);
personList.add(person);
index++;
return person;
}
return personList.get(index++);
}
public static void main(String[] args) {
//创建一个有2个容量的池子
Test test=Test.init(2);
for(int i=0;i<4;i++){
System.out.println(test.getPerson().getName()+"正在干活");
}
}
}
//多例模式创建的对象
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}