单例:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
对于代码中一些不需要区分的对象,如果每次使用都 new 出一个,会太占用内存,所以我们有必要将该对象设计成单例模式。
单例模式中有“饿汉式”和“懒汉式”两种写法。下面进行举例说明。
举例说明
编写一个 Singleton
类
public class SingleTon {
public SingleTon() {
System.out.println("---new---");
}
}
饿汉模式
- 定义一个静态的变量 Singleton,直接初始化
- 定义一个静态方法返回单例对象
public class TestHungarySingleton {
/**
*
* 饿汉模式:初始化时直接创建对象,不会有线程安全问题
* */
private static SingleTon s1 = new SingleTon();
public static SingleTon getS1() {
return s1;
}
public static void main(String[] args) {
test();
}
public static void test(){
for (int i = 0; i < 500; i++) {
new Thread(){
@Override
public void run() {
getS1();
}
}.start();
}
}
}
// log 日志
---new---
我们发现即使在多线程的情况下,也能保证内存中只有一个 Singleton 实例存在。
懒汉模式
- 声明一个静态的变量 Singleton,但不进行初始化
- 定义一个静态方法返回单例对象,在方法中进行初始化
public class TestLazySingleton {
/**
* 懒汉模式:初始化不创建对象,在第一次调用时创建对象
*
* */
private static SingleTon s2;
// 线程不安全写法,有可能创建多个s2实例
public static SingleTon getS2() {
if (s2 == null) {
s2 = new SingleTon();
}
return s2;
}
public static void main(String[] args) {
test();
}
static void test() {
for (int i = 0; i < 500; i++) {
new Thread(){
@Override
public void run() {
getS2();
}
}.start();
}
}
}
// log 日志
---new---
---new---
---new---
---new---
通过日志,我们看到 Singleton 对象被多次创建。在高并发 的多线程情况下,这种方式显然不能满足单例的要求,于是就有了“双重锁”写法。
双重锁的具体写法是,在获取对象的时候,先判断该对象是否为空,不为空直接返回,否则就对该对象进行加锁,然后再次需要判断是否为空。具体代码如下:
public class TestLazySingleton {
/**
* 懒汉模式:初始化不创建对象,在第一次调用时创建对象
*
* */
private static SingleTon s2;
// 线程不安全写法,有可能创建多个s2实例
public static SingleTon getS2() {
if (s2 == null) {
s2 = new SingleTon();
}
return s2;
}
// 线程安全写法:双重锁
public static SingleTon getS20() {
if (s2 == null) {
synchronized (SingleTon.class) {
if (s2 == null) {
s2 = new SingleTon();
}
}
}
return s2;
}
public static void main(String[] args) {
test();
}
static void test() {
for (int i = 0; i < 500; i++) {
new Thread(){
@Override
public void run() {
// getS2();
getS20();
}
}.start();
}
}
}
// log 日志
---new---
如果吧上述代码中第二个判空条件去掉,测试后会发现结果不是单例模式。
为什么会出现这样的情况?
假设现场A、B同时请求加锁,此时两个线程都已经通过了第一个为空的判断。假设A线程先加锁,B线程等待,然后A线程去创建对象,A释放锁后,B线程加锁,又创建了一个对象。
文章只是作为个人记录学习使用,如有不妥之处请指正,谢谢。
参考文章