0.单例模式知识体系
1.单例模式概论
1)引论
古代一个国家皇帝只能有一个,如果多了,国家就会出问题;现代一个家庭老婆只能有一个,如果多了,家就会出问题(现在新闻上还时不时爆出妻子与小三街头互殴的事情),弄得爷们儿不知道怎么办了;有些时候,软件中某些类的对象也只能有一个,多了软件就会出问题(如造成不一致的结果)或浪费资源,如配置文件、工具类、线程池、缓存、日志对象等。
2)定义
一个类有且仅有一个实例,并且自行实例化向整个系统提供。单例模式有一个统一规则(即单例模型),而根据规则执行情况又可分为饿汉模式与懒汉模式,下面展开详细论述。
2.单例模型与饿汉模式与懒汉模式
0)单例模型
(1)私有化构造器,因为创建实例需要构造器,为保证外界不能创建实例,所以私有化,即private修饰。
(2)内部创建或声明实例,私有化构造器后,外界无法创建实例,需要内部创建。
(3)提供获取方法,上面实例创建后存放在私有字段中,不允许外界访问,此时就需要提供类方法获取实例。
1)饿汉模式
所谓饿汉就是感觉类的实例像是食物,即使用户不获取也要先得到。
(1)私有化构造器:private Singleton(){}
(2)内部创建实例:private static final Singleton instance = new Singleton();
(3)提供获取方法:public static Singleton getInstance();
下面是详细代码:
class Singleton {
//2.内部创建实例
private static final Singleton instance = new Singleton();
//1.私有化构造器
private Singleton(){}
//3.提供获取方法
public static Singleton getInstance(){
return instance;
}
//成员方法
public void show(){
System.out.println("单例模式的饿汉模式");
}
}
public class SingletonTest{
public static void main(String[] args){
Singleton.getInstance().show();
}
}
2)懒汉模式
所谓懒汉就是直到用户获取才会创建,很懒,不会早创建一会儿。
(1)私有化构造器:private Singleton(){}
(2)内部声明实例:private static Singleton instance;注意只是声明
(3)提供获取方法:if(instance == null){ instance = new Singleton;} return instance;
class Singleton {
//2.内部声明实例
private static Singleton instance;
//1.私有化构造器
private Singleton(){}
//3.提供获取方法
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
//成员方法
public void show(){
System.out.println("单例模式的懒汉模式");
}
}
public class SingletonTest{
public static void main(String[] args){
Singleton.getInstance().show();
}
}
3.饿汉模式与懒汉模式区别
1)饿汉模式
(1)加载慢,运行快,因为类加载时就会创建对象,不管用户是否使用。
(2)线程安全的。
2)懒汉模式
(1)加载快,运行慢,加载时没有创建对象。
(2)线程不安全的,多线程时有共享数据并且访问此共享数据的代码有多条,可能就会因为CPU切换的随机性而造成构造器的多次调用使不再单例。
如下面代码,多运行几次就会发现构造器会被多次调用
class Singleton
{
//2.内部声明实例
private static Singleton instance;
//1.私有化构造器
private Singleton(){}
//3.提供获取方法
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
System.out.println(Thread.currentThread().getName()+"调用一次构造器");
}
return instance;
}
//成员方法
public void show(){
System.out.println("单例模式的懒汉模式");
}
}
class User implements Runnable{
public void run(){
for(int i =0; i < 3; i++){
Singleton.getInstance().show();
}
}
}
public class SingletonTest{
public static void main(String[] args){
User u = new User();
Thread t0 = new Thread(u);
Thread t1 = new Thread(u);
Thread t2 = new Thread(u);
t0.start();
t1.start();
t2.start();
}
}
可以用双if和同步代码块方式来解决此问题。
//采用双if与同步代码块来解决上述问题
class Singleton {
//2.内部声明实例
private static Singleton instance;
//1.私有化构造器
private Singleton(){}
//3.提供获取方法
public static Singleton getInstance(){
//双重if
if(instance == null){
synchronized(Singleton.class){
//同步代码块,注意因为方法为静态的,不能使用this作为同步锁
if(instance == null){
instance = new Singleton();
System.out.println(Thread.currentThread().getName()+"调用一次构造器");
}
}
}
return instance;
}
//成员方法
public void show(){
System.out.println("单例模式的懒汉模式");
}
}
class User implements Runnable{
public void run(){
for(int i =0; i < 3; i++){
Singleton.getInstance().show();
}
}
}
public class SingletonTest{
public static void main(String[] args){
User u = new User();
Thread t0 = new Thread(u);
Thread t1 = new Thread(u);
Thread t2 = new Thread(u);
t0.start();
t1.start();
t2.start();
}
}