单例模式是什么
创造型设计模式,负责自己创建自己的对象,构造器私有化,不允许外界直接new自己,确保每一次都只有单个对象被创建,这个类提供了一种访问自身对象的一个入口,可以直接访问。优点是可以在内存中只有一个实例,减少了内存的开销,尤其是频繁创建和销毁实例;还有对资源的多重占用(例如写io操作)。缺点就是不能继承,没有接口,与单一职责冲突,一个类只关心自己创建自己对象的逻辑,不关心外面如何实例化。
何时使用单例模式
当整个软件系统中,对某个类只能存在一个实例对象的情况下我们可以使用单例模式。具体使用那种单例,需要结合业务场景。
单例模式三个特点
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
JDK中Runtime使用单例模式
饿汉式
单例模式实现的几种方式
1、懒汉试
/** * 懒加载,第一次调用才初始化,避免内存浪费 */ public class TestLazySingleton { private static TestLazySingleton testLazySingleton; private TestLazySingleton(){} //线程不安全,单线程正常工作,多线程情况下并非单例 public static TestLazySingleton getInstance(){ if (testLazySingleton == null) return testLazySingleton = new TestLazySingleton(); return testLazySingleton; } //线程安全,加锁,效率低 public static synchronized TestLazySingleton getInstanceSafe(){ if (testLazySingleton == null) return testLazySingleton = new TestLazySingleton(); return testLazySingleton; } }
2、饿汉试
/** * 饿汉试:比较常用,非懒加载,项目启动即加载(非使用才加载),浪费内存容易产生垃圾对象。 * 它基于 classloader 机制避免了多线程的同步问题 */ public class TestHungrySingleTon{ private static TestHungrySingleTon testHungrySingleTon = new TestHungrySingleTon(); private TestHungrySingleTon(){} public static TestHungrySingleTon getInstance(){ return testHungrySingleTon; } }
3、双重锁/校验机制
/** * 双重校验机制:懒加载机制,线程安全,可在多线程保持高性能 */ public class TestDoubleCheckedLocking{ //此处使用volatile是防止类初始化是发生指令重排,导致对象初始化半成品对象就被其他线程使用了 private volatile static TestDoubleCheckedLocking testDoubleCheckedLocking; private TestDoubleCheckedLocking (){} public static TestDoubleCheckedLocking getSingleton() { if (testDoubleCheckedLocking == null) { synchronized (TestDoubleCheckedLocking.class) { if (testDoubleCheckedLocking == null) { testDoubleCheckedLocking = new TestDoubleCheckedLocking(); } } } return testDoubleCheckedLocking; } }
4、登记式/静态内部类
/** * 登记式/静态内部类:懒加载,节省内存。线程安全,利用了 classloader 机制来保证初始化 instance 时只有一个线程。 */ public class TestRegistrationSingleton{ private TestRegistrationSingleton(){} private static class GetRegistrationSingletonUtil{ private static final TestRegistrationSingleton SINGLETON_INSTANCE = new TestRegistrationSingleton(); } public static final TestRegistrationSingleton getInstance(){ return GetRegistrationSingletonUtil.SINGLETON_INSTANCE; } }
5、枚举
public enum TestEnumSingleton { INSTANCE; public void whateverMethod() { } public static void main(String[] args) { TestEnumSingleton testEnumSingleton = TestEnumSingleton.INSTANCE; TestEnumSingleton testEnumSingletonV2 = TestEnumSingleton.INSTANCE; CurrentExistingSingleton currentExistingSingleton = CurrentExistingSingleton.SingletonEnum.SINGLETON.getInstance(); CurrentExistingSingleton currentExistingSingletonV2 = CurrentExistingSingleton.SingletonEnum.SINGLETON.getInstance(); //都是true System.out.println(testEnumSingleton == testEnumSingletonV2); System.out.println(currentExistingSingleton == currentExistingSingletonV2); } } /** * 对现存类的加入单例功能的扩展 */ class CurrentExistingSingleton { private CurrentExistingSingleton() { } public static enum SingletonEnum { SINGLETON; private CurrentExistingSingleton instance = null; private SingletonEnum() { instance = new CurrentExistingSingleton(); } public CurrentExistingSingleton getInstance() { return instance; } } }
使用场景
一般情况下,不建议使用第 1 种懒汉方式,建议使用第 2 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 4 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第5 种枚举方式。如果有其他特殊的需求,可以考虑使用第 3 种双检锁方式。
单例模式实践
参考https://blog.csdn.net/lovelion/article/details/7420885文章
实践负载均衡器的设计与实现
将负载均衡器LoadBalancer设计为单例类,其中包含一个存储服务器信息的集合serverList,每次在serverList中随机选择一台服务器来响应客户端的请求
import java.util.ArrayList; import java.util.List; import java.util.Random; /** * 使用单例实现简单的负载均衡功能 */ public class TestLoadBalancer { //懒加载模式 private static TestLoadBalancer testLoadBalancer; //服务器集合 private List serverList; //私有化构造,禁止外界直接new实例 private TestLoadBalancer(){ serverList = new ArrayList(); } /** * 使用枚举:在SingletonEnum中定义了枚举类型的实例对象Singleton, * 再按照单例模式的要求在其中定义一个Singleton类型的对象instance,其初始值为null; * 我们需要将SingletonEnum的构造函数改为私有的,在私有构造函数中创建一个Singleton的实例对象; * 最后在getInstance()方法中返回该对象。 */ public static enum SingletonEnum{ SINGLETON; private SingletonEnum() { testLoadBalancer = new TestLoadBalancer(); } public TestLoadBalancer getInstance() { return testLoadBalancer; } } //增加服务器 public void addServer(String server) { serverList.add(server); } //删除服务器 public void removeServer(String server) { serverList.remove(server); } //使用Random类获取服务器 public String getServer(){ Random random = new Random(); int i = random.nextInt(serverList.size()); return String.valueOf(serverList.get(i)); } public void testSimulationClient(){ TestLoadBalancer clientV1 = SingletonEnum.SINGLETON.getInstance(); TestLoadBalancer clientV2 = SingletonEnum.SINGLETON.getInstance(); TestLoadBalancer clientV3 = SingletonEnum.SINGLETON.getInstance(); TestLoadBalancer clientV4 = SingletonEnum.SINGLETON.getInstance(); TestLoadBalancer clientV5 = SingletonEnum.SINGLETON.getInstance(); //判断服务器是否一致 if (clientV1 == clientV2 && clientV2 == clientV3 && clientV3 == clientV4 && clientV4 == clientV5){ System.out.println("服务器负载均衡器具有唯一性!"); } //增加服务器 clientV1.addServer("Server 1"); clientV1.addServer("Server 2"); clientV1.addServer("Server 3"); clientV1.addServer("Server 4"); //模拟客户端请求的分发 for (int i = 0; i < 10; i++) { String server = clientV1.getServer(); System.out.println("分发请求至服务器: " + server); } } public static void main(String[] args) { new TestLoadBalancer().testSimulationClient(); } }