Java之小白正确认识单例
开发工具与关键技术:Java[MyEclipse 10]
作者:谢景
撰写时间:2019-5-6
在刚学Java的单例的时候,我相信很多的伙伴跟我一样都是似懂非懂的感觉。
下面是我老师给我讲的单例的作用和适有场景
首先,介绍一下单例,单例有常用下面几种:
-
饿汉式:
特点:线程安全,调用效率高,但是不能延时加载 -
懒汉式:
特点:线程安全,调用效率不高,但是能延时加载 -
Double CheckLock实现单例:
Double CheckLock也就是双重锁判断机制(由于JVM底层模型原因,偶尔会出问题, 不建议使用),是在懒汉式单例上发展而来 -
静态内部类模式:
特点:线程安全,调用效率高,可以延时加载 -
枚举类:
特点:线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用
学完单例之后我们都会想单例的几个问题:
- 单例模式跟普通的静态变量、静态方法有什么不一样?
- 单例模式不用建立连接就可以实现资源相互通信是怎么回事?
- 单例为什么能实现只能单用户调用,其他用户排队的?
之后对单例做了一系列的实验,对上述问题有了一定的回答能力。
首先,单例的特征:
只能实例化一次
单例模式是自己实例化自己
私有构造器
用静态方法返回自己给外部调用
对于单例模式跟普通的静态变量、静态方法有什么不一样?
我觉得单例跟普通静态方法最大的区别就是单例可以私有化方法和变量,可以做到外部无法访问单例内部的成员变量和方法,可以确保了数据的安全性;而普通静态变量的值是所有人可调用,可随意修改值,所以保证不了数据的安全性。
对于单例模式不用建立连接就可以实现资源相互通信是怎么回事?
因为单例模式是静态成员实例化的,所以一创建就会存在于内存中,所以可以在单例模式中写一个静态成员变量存放个用户的资源,这样就可以实现各用户间的资源共享、通信之类的操作,还可以将变量变成私有,这样既确保了资源可以共享也确保了资源的安全。
对于单例为什么能实现只能单用户调用,其他用户排队的?
对于这个问题一直困扰我很久,一开始我以为单单只用单例就可以实现生成唯一序列环境,也就是只能单用户调用,其他用户排队的功能,但是后来发现是我理解错了,单例只是适合有在这种场景,并不是单凭单例就可以实现这种功能。
为什么这么说呢,用结果说话,下面我用多线程模拟多用户同时调用单例的场景
首先,我创建了一个饿汉式单例实现生成单号,如下:
class Ehan {
private static final Ehan ehan = new Ehan();
private int i = 0;
private static final Object object = new Object();
private Ehan() {};
public String ddd1(int c) {
this.i += 1;
int a = i;
String str = "TS000" + a + "/"+ new SimpleDateFormat("yyyy-MM-dd").format(new Date());
return str;
}
public static Ehan ddd() {
return ehan;
}
}
然后我继承一个线程的类ChThread,调用单例获取单号
class ChThread extends Thread {
public int c = 0;
ChThread(int b) {
this.c = b;
}
public void run() {
Ehan ehan = Ehan.ddd();
System.out.println(ehan.ddd1(c));
}
}
最后用方法调用线程类
public class ThreadDemo {
public static void main(String[] args) {
ThreadDemo demo = new ThreadDemo();
demo.ddd(1);
demo.ddd(2);
demo.ddd(3);
demo.ddd(4);
demo.ddd(5);
demo.ddd(6);
demo.ddd(7);
}
public void ddd(int d) {
ChThread chThread = new ChThread(d);
chThread.start();
}
}
在上面我用单例实现生成单号,在单例中我声明了一个成员变量,每被调用一次就会自加一,这样就可以使每个单号都不一样,现在我们假设一下,如果单凭单例就可以实现生成唯一序列环境,那么每个线程获得的单号肯定不一样,现在我来执行代码。
结果如下:
很明显,最后两条单号一样,证明所有线程是同时调用单例的,而此时我们要实现生成唯一序列环境,我们需要加锁或者加线程安全才能实现。