概述
分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net
1. 饿汉式单例
饿汉式单例是指在方法调用前,实例就已经创建好了。
package chimomo.learning.java.designpattern.singleton.variousimplementations;
/**
* @author Created by Chimomo
*/
public class EagerSingleton {
// 创建私有静态实例,意味着这个类第一次使用的时候就会进行创建
private static EagerSingleton instance = new EagerSingleton();
// 将 new EagerSingleton() 堵死
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return instance;
}
}
来看一下饿汉式单例在多线程下的执行情况,多线程的执行代码如下:
package chimomo.learning.java.designpattern.singleton.variousimplementations;
/**
* @author Created by Chimomo
*/
public class ThreadSafetyTest extends Thread {
public static void main(String[] args) {
ThreadSafetyTest[] threads = new ThreadSafetyTest[30];
for (int i = 0; i < threads.length; i++) {
threads[i] = new ThreadSafetyTest();
}
for (int j = 0; j < threads.length; j++) {
threads[j].start();
}
}
@Override
public void run() {
System.out.println(EagerSingleton.getInstance().hashCode());
}
}
代码执行结果如下:
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
69186387
从执行结果可以看出,实例变量hashCode值一致,说明对象是同一个,饿汉式单例是线程安全的。
2. 懒汉式单例
懒汉式单例是指在方法调用获取实例时才创建实例,因为相对“饿汉式”显得不急迫,所以被叫做“懒汉式”单例。
package chimomo.learning.java.designpattern.singleton.variousimplementations;
/**
* @author Created by Chimomo
*/
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {
}
public static LazySingleton getInstance() {
try {
if (instance == null) {
Thread.sleep(300);
instance = new LazySingleton();
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return instance;
}
}
上述的懒汉式单例不是线程安全的,可以用上述的多线程代码执行,会发现执行结果如下:
2049367639
1931775225
140919816
1787716501
67641385
841698118
418194775
1442414714
664286818
924507082
1741100778
1549208166
574926395
1553816201
999575674
1603532086
1750416013
1386313329
33689776
1272861279
980142845
754568537
123504074
228907269
2034349848
508832262
1700405589
1174385069
2131079504
1058874213
3. 线程安全的懒汉式单例
3.1 方法中声明synchronized关键字
出现非线程安全问题,是由于多个线程可以同时进入getInstance方法,那么只需要对该方法增加synchronized锁同步即可:
package chimomo.learning.java.designpattern.singleton.variousimplementations;
/**
* @author Created by Chimomo
*/
public class ThreadSafeLazySingleton {
private static ThreadSafeLazySingleton instance = null;
private ThreadSafeLazySingleton() {
}
public static synchronized ThreadSafeLazySingleton getInstance() {
try {
if (instance == null) {
Thread.sleep(300);
instance = new ThreadSafeLazySingleton();
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return instance;
}
}
大家可以自行验证其线程安全性。然而,这种实现方式的运行效率会很低。既然同步方法效率低,那我们考虑使用同步代码块来实现。
3.2 同步代码块
package chimomo.learning.java.designpattern.singleton.variousimplementations;
/**
* @author Created by Chimomo
*/
public class SynchronizedBlockLazySingleton {
private static SynchronizedBlockLazySingleton instance = null;
private SynchronizedBlockLazySingleton() {
}
public static SynchronizedBlockLazySingleton getInstance() {
try {
synchronized (SynchronizedBlockLazySingleton.class) {
if (instance == null) {
Thread.sleep(300);
instance = new SynchronizedBlockLazySingleton();
}
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return instance;
}
}
这里的实现能够保证多线程并发下的线程安全性,但是这样的实现将全部的代码都锁上了,同样的效率很低下。
3.3 针对某些重要的代码来进行单独的同步(可能非线程安全)
针对某些重要的代码进行单独的同步,而不是全部进行同步,可以极大的提高执行效率。
package chimomo.learning.java.designpattern.singleton.variousimplementations;
/**
* @author Created by Chimomo
*/
public class SynchronizedImportantBlockLazySingleton {
private static SynchronizedImportantBlockLazySingleton instance = null;
private SynchronizedImportantBlockLazySingleton() {
}
public static SynchronizedImportantBlockLazySingleton getInstance() {
try {
if (instance == null) {
Thread.sleep(300);
synchronized (SynchronizedImportantBlockLazySingleton.class) {
instance = new SynchronizedImportantBlockLazySingleton();
}
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return instance;
}
}
同样使用前面的运行多线程的代码来验证,运行结果如下:
999575674
33689776
1272861279
69186387
1031548957
903341402
2131079504
1883420913
499131126
1174385069
256150948
1872197595
531436928
1700405589
1543370641
1604929816
1549208166
1052936304
754568537
1460486463
1896493143
1201841772
635845204
2118349189
186668687
1365888608
1684444739
1770731361
123504074
1030177192
从运行结果来看,这样的方法进行代码块同步,代码的运行效率是能够得到提升,但是却没能保住线程的安全性。看来还得进一步考虑如何解决此问题。
3.4 Double Check Locking 双检查锁机制(推荐)
为了达到线程安全,又能提高代码执行效率,我们可以采用DCL的双检查锁机制来完成,代码实现如下:
package chimomo.learning.java.designpattern.singleton.variousimplementations;
/**
* @author Created by Chimomo
*/
public class DoubleCheckLockingLazySingleton {
// 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的
private static volatile DoubleCheckLockingLazySingleton instance = null;
// 也是先堵死 new Singleton() 这条路
private DoubleCheckLockingLazySingleton() {
}
public static DoubleCheckLockingLazySingleton getInstance() {
try {
if (instance == null) {
Thread.sleep(300);
// 加锁
synchronized (DoubleCheckLockingLazySingleton.class) {
// 这一次判断也是必须的,不然会有并发问题
if (instance == null) {
instance = new DoubleCheckLockingLazySingleton();
}
}
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return instance;
}
}
用前面的多线程代码进行验证,结果如下:
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
355159803
从运行结果来看,此方法保证了多线程并发下的线程安全性。这里在声明变量时使用了volatile关键字来保证其线程间的可见性;在同步代码块中使用二次检查,以保证其不被重复实例化。集合其二者,这种实现方式既保证了其高效性,也保证了其线程安全性。
4. 嵌套类
嵌套类最经典,以后大家就用它吧:
public class Singleton {
private Singleton() {}
// 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性
private static class Holder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Holder.instance;
}
}
注意,很多人都会把这个嵌套类说成是静态内部类,严格地说,内部类和嵌套类是不一样的,它们能访问的外部类权限也是不一样的。
5. 枚举
最后,我们说一下枚举,枚举很特殊,它在类加载的时候会初始化里面的所有的实例,而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。
虽然我们平时很少看到用枚举来实现单例,但是在 RxJava 的源码中,有很多地方都用了枚举来实现单例。
最后
以上就是怕孤独春天为你收集整理的设计模式 - 创建型设计模式 - 单例模式(Java)1. 饿汉式单例2. 懒汉式单例3. 线程安全的懒汉式单例4. 嵌套类5. 枚举的全部内容,希望文章能够帮你解决设计模式 - 创建型设计模式 - 单例模式(Java)1. 饿汉式单例2. 懒汉式单例3. 线程安全的懒汉式单例4. 嵌套类5. 枚举所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复