单例模式
定义:单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
通俗地说,使用单例模式确保一个类从始至终只被new一次,也就是只产生一个对象。
那么就该思考了,怎么样才能让一个类只被new一次呢?那我们先回忆如何产生一个对象,java对象的产生都是通过构造函数,所以呢思路来了,把构造器私有,外部就没办法去new产生对象了?以为这就完了?不存在的这只是一小步,具体流程及方式下面慢慢说来:
单例模式主要分为两种模式:
- 懒汉式
- 饿汉式
首先说一下懒汉式:
懒汉式见名知意,比较懒,为何比较懒呢,因为在用的时候才去产生一个对象。是不是够懒得。好了接下来看一下具体如何实现:
/**
* 单例模式
* @author 花笺
*/
public class King {
//古时候每朝只有一个 Emperor皇帝
private static King instance = null;
private King (){}
public static King getInstance (){
if (instance == null)
instance = new King();
return instance;
}
}
测试一下写的代码:
public static void main(String[] args) {
King lishiming = King.getInstance();
King lilongji = King.getInstance();
System.out.println("对象相等 :" +(lishiming == lilongji));
}
结果为:
但是对于这种情况就会有问题
Thread A = new Thread(new Runnable() {
@Override
public void run() {
King lishiming = King.getInstance();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程A产生的对象 :" + lishiming.toString());
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
King lilongji = King.getInstance();
System.out.println("线程B产生的对象:" + lilongji.toString());
}
});
A.start();
B.start();
产生的结果:
但是这样会存在一个问题,对于单线程程序这个完全没有问题。但是对于多线程程序来说,这会出问题的!对于多个线程同时访问的时候就不能保证只产生一个对象了,这就是线程不安全。
既然是线程不安全那就给他加个锁,让他线程安全,当然加锁的粒度越小越好,这里只为简单的演示。
//古时候每朝只有一个 Emperor皇帝
private static King instance = null;
private King (){}
public synchronized static King getInstance (){
if (instance == null)
instance = new King();
return instance;
}
//双重判断
public static King getInstance(){
if (instance == null){
synchronized (King.class) {
if (instance == null)
instance = new King();
}
}
return instance;
}
实验结果:
这样不就ok了?是解决了线程不安全的问题,但是又会存在另一个问题,效率问题,加锁之后在一个线程访问时其他的线程都在等待会消耗很多的时间。
饿汉式:
也是见名知意,饿汉很饿,很着急,所以在声明对象是就对产生实例
/**
* 饿汉式单例
* @author 花笺
*/
public class Emperor {
private static Emperor instance = new Emperor();
private Emperor (){}
public static Emperor getInstance (){
return instance;
}
}
测试类:
public static void main(String[] args) {
Thread A = new Thread(new Runnable() {
@Override
public void run() {
Emperor lishiming = Emperor.getInstance();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程A产生的对象 :" + lishiming.toString());
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
Emperor lilongji = Emperor.getInstance();
System.out.println("线程B产生的对象:" + lilongji.toString());
}
});
A.start();
B.start();
结果:
饿汉式对象在类加载的时候就会完成创建,不存在线程安全问题,那么是不是就是饿汉式一定比懒汉式要好呢?饿汉式可是天生线程安全的!但是也存在一个问题,那就是如果这个对象从不用呢,饿汉式从类加载的同时就会产生实例,占用内存,如果不用则会浪费这段内存。所以二者各有利弊。
总之单例模式只会产生一个对象,这种模式还是使用比较普遍的,windows系统中的任务管理器等。
更多内容请访问我的博客:天空日记