Java单例模式再加强——按组多单例
最近要使用alibaba的rocket mq(我们公司对其进行了封装,使其运行在dotNet平台上,Java还是和原生的差不多,涉及公司的内容本文不会提及),其中 在生产者组这一块,建议是用单例模式的。但是其中又建议一个组(group)使用一个实例,这样仅仅单例模式就不行了,所以要进行改动,我们的目标就是“一个group使用一个单例”。
其实简单点,多封装几个不同的单例类就行了,一个组用一个类。但是这显然不是一个好主意,于是我们来考虑用另一种方式。
首先要将 group 这个概念抽出来,它是变量,接下来封装不变的代码。
我们先看看代码是什么样的:
/**
* TurboMQ 消息生产者管理器
*/
public class MqProducer { private DefaultMQProducer currentMQProducer; private static Map<String, MqProducer> producerMap = new ConcurrentHashMap<>(3);
private static final Object lock = new Object(); private MqProducer(String group) throws MQClientException {
if (!Validator.isNotNullAndVisible(group)) {
throw new NullPointerException("Group名称不能为空!");
} currentMQProducer = new DefaultMQProducer(group);
currentMQProducer.setNamesrvAddr(“1.1.1.1”);
currentMQProducer.start();
} public static MqProducer instance(String group) throws MQClientException {
if (!Validator.isNotNullAndVisible(group)) {
throw new NullPointerException("Group名称不能为空!");
} if (producerMap.get(group) == null) {
synchronized (lock) {
if (producerMap.get(group) == null) {
producerMap.put(group, new MqProducer(group));
}
}
}
return producerMap.get(group);
} public SendResult send(String topic, String tag, String body) throws UnsupportedEncodingException, InterruptedException, RemotingException, MQClientException, MQBrokerException {
if (!Validator.isNotNullAndVisible(topic, tag, body)) {
throw new NullPointerException("请检查参数是否为空,topic,tag,body");
}
Message message = new Message(topic, tag, body.getBytes("UTF-8"));
return currentMQProducer.send(message);
} public static void shutdownAll() {
producerMap.forEach((key, value) -> {
value.shutdown();
});
} public void shutdown() {
currentMQProducer.shutdown();
} }
我们的解决思路,就是使用 Map 让 group 和实例一一对应起来。
这些代码中你可能需要注意的点是:
1 线程安全的 ConcurrentHashMap 以及要设置初始容量
private static Map<String, MqProducer> producerMap = new ConcurrentHashMap<>(3);
2 instance方法中的两层 if 判断
在 synchronized(lock)锁住之前可能有多个线程了解到当前组是null,都去请求锁,当第一个线程new了新生产者之后,下一个进程进来就不会再new一个新的生产者了。
public static MqProducer instance(String group) throws MQClientException {
if (producerMap.get(group) == null) {
synchronized (lock) {
if (producerMap.get(group) == null) {
producerMap.put(group, new MqProducer(group));
}
}
}
return producerMap.get(group);
}
题外话:
为什么要抛异常?
因为此处是通用代码,通用代码不应处理业务逻辑,而且不该隐蔽错误的发生,要让业务逻辑去确保参数没问题。
Java单例模式再加强——按组多单例的更多相关文章
- Java线程和多线程(五)——单例类中的线程安全
单例模式是最广泛使用的创建模式之一.在现实世界之中,诸如Databae的连接或者是企业信息系统(EIS)等,通常其创建都是受到限制的,应该尽量复用已存在对象而不是频繁创建销毁.为了达到这个目的,开发者 ...
- JAVA笔记5__构造块、静态块/单例设计模式/继承/final关键字/super关键字
public class Main { { //构造块(在构造对象时调用,先于构造方法执行) System.out.println("我是构造块!"); } static{ //静 ...
- Java面向对象 Main函数 静态的应用 单例设计模式
Java面向对象 Main函数 静态的应用与单例设计模式 知识概要 (1)Main函数的细解 (2)静态的应用,静态变量,静态代码块,静态函数 (3)单例设计模式 1.M ...
- Java静态变量的用法:伪单例
这几天遇到一个问题,一个Service里有一个map,但是这个Service有别的继承,于是每一个Service都会创建一个map,但是这个map应该是公用的,于是就有问题了...(按结构说Servi ...
- Spring单例Bean和线程安全
Spring的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢?例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框 ...
- Java 单例模式详解
概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. ...
- 0013 Java学习笔记-面向对象-static、静态变量、静态方法、静态块、单例类
static可以修饰哪些成员 成员变量---可以修饰 构造方法---不可以 方法---可以修饰 初始化块---可以修饰 内部类(包括接口.枚举)---可以修饰 总的来说:静态成员不能访问非静态成员 静 ...
- 转 java 类 单例
转 单例概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一 ...
- Java单例模式深入详解
原文地址:http://www.cnblogs.com/hxsyl/ 仅作为笔记收藏…… 一.问题引入 偶然想想到的如果把Java的构造方法弄成private,那里面的成员属性是不是只有通过stati ...
随机推荐
- C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(下)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(下)),不对的地方欢迎指出与交流. 章节出自<Professional C ...
- C# 总结const、 readonly、 static三者区别:
总结const. readonly. static三者区别: (有人问我,看似简单,我也没能立刻回答出来,总结一下,分享一下.) const:静态常量,也称编译时常量(compile-time con ...
- JavaScript中的this关键字的用法和注意点
JavaScript中的this关键字的用法和注意点 一.this关键字的用法 this一般用于指向对象(绑定对象); 01.在普通函数调用中,其内部的this指向全局对象(window); func ...
- strpos、 strstr、 substr三个函数的对比讲解
mixed strpos ( string $haystack , mixed $needle [, int $offset = 0 ] ) 返回 needle 在 haystack 中首次出现的数字 ...
- java调用控制台命令
String shell = "ping www.baidu.com";//需要执行的命令 BufferedReader br = null;try { Process p = R ...
- Android通过Apk插件调起微信支付
App对接微信调起微信支付需要在微信平台注册,鉴别的标识就是App的包名,所以将申请的包名单独打包成一个Apk文件,则在其他的App调起此Apk的时候同样可以起到调用微信支付的功能.这样就实现了调起微 ...
- 读书笔记 effective c++ Item 26 尽量推迟变量的定义
1. 定义变量会引发构造和析构开销 每当你定义一种类型的变量时:当控制流到达变量的定义点时,你引入了调用构造函数的开销,当离开变量的作用域之后,你引入了调用析构函数的开销.对未使用到的变量同样会产生开 ...
- jquery及原生javascript对jsonp解决跨域问题实例详解
jquery方式 前端: $.ajax({ url: 'http://m.xxx.tv/goLottery', data: { data: data }, type: 'GET', dataType: ...
- Laravel的console使用方法
适用场景:分析数据(日志) php artisan make:console 你的命令类名 示例: php artisan make:console Check 在\app\Console\Comma ...
- 信号处理——Hilbert变换及谱分析
作者:桂. 时间:2017-03-03 23:57:29 链接:http://www.cnblogs.com/xingshansi/articles/6498913.html 声明:转载请注明出处, ...