附Java/C/C++/机器学习/算法与数据结构/前端/安卓/Python/程序员必读书籍书单大全:
书单导航页(点击右侧 极客侠栈 即可打开个人博客):极客侠栈
①【Java】学习之路吐血整理技术书从入门到进阶最全50+本(珍藏版)
②【算法数据结构+acm】从入门到进阶吐血整理书单50+本(珍藏版)
③【数据库】从入门到进阶必读18本技术书籍网盘吐血整理网盘(珍藏版)
④【Web前端】从HTML到JS到AJAX到HTTP从框架到全栈帮你走更少弯路(珍藏版)
⑤【python】书最全已整理好(从入门到进阶)(珍藏版)
⑥【机器学习】+python整理技术书(从入门到进阶已经整理好)(珍藏版)
⑦【C语言】推荐书籍从入门到进阶带你走上大牛之路(珍藏版)
⑧【安卓】入门到进阶推荐书籍整理pdf书单整理(珍藏版)
⑨【架构师】之路史诗级必读书单吐血整理四个维度系列80+本书(珍藏版)
⑩【C++】吐血整理推荐书单从入门到进阶成神之路100+本(珍藏)
⑪【ios】IOS书单从入门到进阶吐血整理(珍藏版)
--------------------------------------------------------------------------------------------------------------------
什么是单例
单例模式确保某各类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能,每台计算机可以有若干个打印机,但只能有一个Printer spooler,以避免两个打印作业同时输出到打印机中,每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态
单例模式特点
1、单例类只能有一个实例。
2、单例类必须自己创建自己唯一的实例。
3、单例类必须给所有其它对象提供这一实例。
单例模式优缺点
1、单例类只有一个实例
2、共享资源,全局使用
3、节省创建时间,提高性能
单例模式的七种写法
分别是「饿汉」、「懒汉(非线程安全)」、「懒汉(线程安全)」、「双重校验锁」、「静态内部类」、「枚举」和「容器类管理]
1.饿汉式
package com.xuyu.V1;
/**
* author:须臾
*/
public class SingletonV1 {
/**
* 饿汉式
* 优点:先天线程安全,当类初始化的时候就会创建该对象
* 缺点:如果饿汉式使用频繁,可能会影响项目启动效率
*/
private static SingletonV1 singletonV1=new SingletonV1();
/**
* 将构造函数私有化,禁止初始化
*/
private SingletonV1(){}
public static SingletonV1 getInstance(){
return singletonV1;
}
/**
* 测试单例
*/
public static void main(String[] args) {
SingletonV1 instance1 = SingletonV1.getInstance();
SingletonV1 instance2 = SingletonV1.getInstance();
//结果为true,说明保证了单例
System.out.println(instance1==instance2);
}
}
源码分析Runtime
//饿汉式单例
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
..
}
2.懒汉式(线程不安全)
package com.xuyu.V2;
/**
* author:须臾
*/
public class SingletonV2 {
/**
* 饿汉式(线程不安全)
*/
private static SingletonV2 singletonV2;
private SingletonV2(){}
/**
* 创建对象使用
*/
public static SingletonV2 getInstance(){
if(singletonV2==null){
try {
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
singletonV2=new SingletonV2();
}
return singletonV2;
}
/**
* 测试单例
*/
public static void main(String[] args) {
for (int i=0;i<100;i++){
new Thread(new Runnable() {
public void run() {
SingletonV2 instance1 = SingletonV2.getInstance();
System.out.println(Thread.currentThread().getName()+","+instance1);
}
}).start();
}
}
}
输出结果:线程不安全
Thread-1,com.xuyu.V2.SingletonV2@383a0ba
Thread-4,com.xuyu.V2.SingletonV2@d9d8ad0
Thread-0,com.xuyu.V2.SingletonV2@546431f0
Thread-5,com.xuyu.V2.SingletonV2@2858c11c
Thread-22,com.xuyu.V2.SingletonV2@3635f62a
Thread-6,com.xuyu.V2.SingletonV2@48369750
Thread-7,com.xuyu.V2.SingletonV2@2770f418
Thread-3,com.xuyu.V2.SingletonV2@6d9da26a
Thread-13,com.xuyu.V2.SingletonV2@77355386
Thread-10,com.xuyu.V2.SingletonV2@29580e2d
....
Thread-94,com.xuyu.V2.SingletonV2@3945e031
Thread-91,com.xuyu.V2.SingletonV2@5caf9db6
3.懒汉式(线程安全)
package com.xuyu.V3;
/**
* author:须臾
*/
public class SingletonV3 {
/**
* 懒汉式线程安全
*/
private static SingletonV3 singletonV3;
private SingletonV3(){}
/**
* 效率低
*/
public synchronized static SingletonV3 getInstance(){
try {
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
if (singletonV3==null){
System.out.println("创建实例SingletonV3");
singletonV3=new SingletonV3();
}
System.out.println("获取SingletonV3实例");
return singletonV3;
}
/**
* 测试单例
*/
public static void main(String[] args) {
for (int i=0;i<100;i++){
new Thread(new Runnable() {
public void run() {
SingletonV3 instance1 = SingletonV3.getInstance();
System.out.println(Thread.currentThread().getName()+","+instance1);
}
}).start();
}
}
}
输出结果
创建实例SingletonV3
获取SingletonV3实例
Thread-0,com.xuyu.V3.SingletonV3@95458f7
获取SingletonV3实例
Thread-99,com.xuyu.V3.SingletonV3@95458f7
获取SingletonV3实例
Thread-98,com.xuyu.V3.SingletonV3@95458f7
获取SingletonV3实例
Thread-97,com.xuyu.V3.SingletonV3@95458f7
获取SingletonV3实例
....
4.双重检验锁(DCL)
package com.xuyu.V4;
public class SingletonV4 {
/**
* volatile 禁止指令重排序
*/
private static volatile SingletonV4 singletonV4;
private SingletonV4(){}
public static SingletonV4 getInstance(){
if(singletonV4==null){//第一次判断如果没有创建对象就开始加锁
synchronized (SingletonV4.class){
if (singletonV4==null){//当用户抢到锁,判断初始化
System.out.println("第一次开始创建实例对象,获取到锁了");
try {
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
singletonV4=new SingletonV4();
}
}
}
return singletonV4;
}
/**
* 测试单例
*/
public static void main(String[] args) {
for (int i=0;i<100;i++){
new Thread(new Runnable() {
public void run() {
SingletonV4 instance1 = SingletonV4.getInstance();
System.out.println(Thread.currentThread().getName()+","+instance1);
}
}).start();
}
}
}
输出结果:线程安全
第一次开始创建实例对象,获取到锁了
Thread-99,com.xuyu.V4.SingletonV4@383a0ba
Thread-89,com.xuyu.V4.SingletonV4@383a0ba
Thread-92,com.xuyu.V4.SingletonV4@383a0ba
Thread-91,com.xuyu.V4.SingletonV4@383a0ba
....
Thread-8,com.xuyu.V4.SingletonV4@383a0ba
Thread-6,com.xuyu.V4.SingletonV4@383a0ba
Thread-9,com.xuyu.V4.SingletonV4@383a0ba
Thread-12,com.xuyu.V4.SingletonV4@383a0ba
Thread-11,com.xuyu.V4.SingletonV4@383a0ba
Thread-10,com.xuyu.V4.SingletonV4@383a0ba
Thread-15,com.xuyu.V4.SingletonV4@383a0ba
Thread-19,com.xuyu.V4.SingletonV4@383a0ba
Thread-16,com.xuyu.V4.SingletonV4@383a0ba
5.静态内部内形式
package com.xuyu.V5;
/**
* author:须臾
*/
public class SingletonV5 {
private SingletonV5(){
System.out.println("对象初始化...");
}
public static SingletonV5 getInstance(){
return SingletonV5Utils.singletonV5;
}
/**
* 静态内部类方式:能够避免同步带来的效率问题和实现懒加载
*/
public static class SingletonV5Utils{
private static SingletonV5 singletonV5=new SingletonV5();
}
/**
* 测试单例
*/
public static void main(String[] args) {
System.out.println("项目启动成功。。。");
SingletonV5 instance1 = SingletonV5.getInstance();
SingletonV5 instance2 = SingletonV5.getInstance();
System.out.println(instance1==instance2);
}
}
输出结果
项目启动成功。。。
对象初始化...
true
6.枚举形式
package com.xuyu.V6;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* author:须臾
*/
public enum EnumSingleton {
INSTANCE;
// 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破坏
public void add() {
System.out.println("add方法...");
}
/**
* 测试单例
*/
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingleton instance1 = EnumSingleton.INSTANCE;
EnumSingleton instance2 = EnumSingleton.INSTANCE;
System.out.println(instance1==instance2);
Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
EnumSingleton v6 = declaredConstructor.newInstance();
System.out.println(v6==instance1);
}
}
输出结果:反射破坏不了单例
true
Exception in thread "main" java.lang.NoSuchMethodException: com.xuyu.V6.EnumSingleton.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at com.xuyu.V6.EnumSingleton.main(EnumSingleton.java:19)
7.使用容器管理
package com.xuyu.V7;
import java.util.HashMap;
import java.util.Map;
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String, Object>();
public static void registerService(String key, Object instance) {
if (!objMap.containsKey(key)) {
objMap.put(key, instance);
}
}
public static Object getService(String key) {
{
return objMap.get(key);
}
}
}
这种使用SingletonManager 将多种单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
如何防止破坏单例
虽然单例通过私有构造函数,可以实现防止程序猿初始化对象,但是还可以通过反射和序列化技术破坏单例。
1.使用反射技术破坏单例
// 1. 使用懒汉式创建对象
SingletonV3 instance1 = SingletonV3.getInstance();
// 2. 使用Java反射技术初始化对象 执行无参构造函数
Constructor<SingletonV3> declaredConstructor = SingletonV3.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV3 instance2 = declaredConstructor.newInstance();
System.out.println(instance1 == instance2);
如何防止被反射破坏
私有构造函数
private SingletonV3() throws Exception {
synchronized (SingletonV3.class) {
if (singletonV3 != null) {
throw new Exception("该对象已经初始化..");
}
System.out.println("执行SingletonV3无参构造函数...");
}
}
2.使用序列化技术破坏单例
Singleton instance = Singleton.getInstance();
FileOutputStream fos = new FileOutputStream("E:\\code\\Singleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("E:\\code\\Singleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
Singleton singleton2 = (Singleton) ois.readObject();
System.out.println(singleton2==instance)
//返回序列化获取对象 ,保证为单例
public Object readResolve() {
return singletonV3;
}
总结
到这里七中写法都介绍完了,至于选择用哪种形式的单例模式,取决于你的项目本身,是否是有复杂的并发环境,还是需要控制单例对象的资源消耗。