前言:
单例设计模式也是非常常用的设计模式,比如我们所熟知的servlet他在Tomcat中是一个单例设计模式的实现,那到底单例设计模式是什么,有什么用呢?咱们一起来学习
1.什么是单例设计模式
有些对象我们只需要一个,比如配置文件,工具类,线程池,缓存,日志对象等等。如果我们创造多个实例会造成很多问题,比如占用资源过多,不一致的结果等。我们的单例设计模式就是保证实例只有一个,就能很好地避免这些问题
单例设计模式有两种:
- 饿汉式
- 懒汉式
2.饿汉式的实现
public class Singleton {
//1.将构造方法设为私有,不允许外部直接创建对象
private Singleton(){
}
//2.创建一个实例
private static Singleton instance = new Singleton();
//3提供一个用于获取实例的方法
public static Singleton getInstance(){
return instance;
}
}
public class Test {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//看是否指向同一个对象
System.out.println(s1 == s2 ? "是同一个实例" :"不是同一个实例");
}
}
从上面我们知道:
1.将构造方法设为私有,不允许外部直接创建对象
2.创建一个静态私有的实例
3.对外提供一个静态的方法,返回这个实例
4.访问的时候通过类名.静态方法获取
static修饰的是从类加载的时候就加载的,所以说当Singleton类加载的时候就加载了这个实例;我们可以形象地把加载的这个过程称为饿汉,因为他太饿了,所以想快点吃到东西(从类加载的时候就加载进来,迫不及待!imagine一下~)
3.懒汉式
public class Singleton2 {
//将构造方法私有化
private Singleton2(){
}
//声明类的唯一实例
private static Singleton2 instance;
//提供一个对外获取实例的方法
public static Singleton2 getSingleton2() {
if(instance == null)
instance = new Singleton2();
return instance;
}
}
public class Test {
public static void main(String[] args) {
//饿汉式
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//看是否指向同一个对象
System.out.println(s1 == s2 ? "是同一个实例" :"不是同一个实例");
//懒汉式
Singleton2 s3 = Singleton2.getInstance();
Singleton2 s4 = Singleton2.getInstance();
System.out.println(s3 == s4 ? "是同一个实例" :"不是同一个实例");
}
}
从上面我们知道:
1.私有化构造函数
2.声明一个实例
3.提供对外的方法,当第一个用户访问的时候就实例化instance对象,当第二个再访问的时候就不用再创建了,直接返回。看起来是不是很懒?后面的人都不想创建了,都拜托第一个访问的人了!!
4.两者的区别
1.饿汉模式加载类的时候比较慢,但运行的时候获取对象的速度比较快,线程安全
2.懒汉模式加载类的时候比较快,但是在运行时获取对象的速度比较慢,因为我们第一次访问的时候要创建对象,所以比较慢,同时懒汉模式线程是不安全的
3.为什么说饿汉模式的线程是安全的,懒汉模式线程是不安全的?举个例子,现在有两个线程thread1,thread2,他们都访问了上述的两种模式的对象,因为饿汉模式在加载的时候就实例化了,所以取到的值只有唯一一个,而懒汉模式,thread1有可能在if(instance == null)的时候还没开始实例化,thread2也加进来,这个时候就会有两个对象
测试饿汉式
public class TestThread implements Runnable {
@Override
public void run() {
// 测试饿汉式
Singleton s1 = Singleton.getInstance();
System.out.println("对象被创建" + s1 + " 当前线程" + Thread.currentThread().getName());
System.out.println("hashCode" + s1.hashCode());
}
public static void main(String[] args) {
TestThread t1 = new TestThread();
TestThread t2 = new TestThread();
new Thread(t1).start();
new Thread(t2).start();
}
}
测试懒汉式
public class TestThread implements Runnable {
@Override
public void run() {
// 测试饿汉式
Singleton2 s2 = Singleton2.getInstance();
System.out.println("对象被创建" + s2 + " 当前线程" + Thread.currentThread().getName());
System.out.println("hashCode" + s2.hashCode());
}
public static void main(String[] args) {
TestThread t1 = new TestThread();
TestThread t2 = new TestThread();
new Thread(t1).start();
new Thread(t2).start();
}
}
Look看到效果了吧!