最近要使用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单例模式再加强——按组多单例的更多相关文章

  1. Java线程和多线程(五)——单例类中的线程安全

    单例模式是最广泛使用的创建模式之一.在现实世界之中,诸如Databae的连接或者是企业信息系统(EIS)等,通常其创建都是受到限制的,应该尽量复用已存在对象而不是频繁创建销毁.为了达到这个目的,开发者 ...

  2. JAVA笔记5__构造块、静态块/单例设计模式/继承/final关键字/super关键字

    public class Main { { //构造块(在构造对象时调用,先于构造方法执行) System.out.println("我是构造块!"); } static{ //静 ...

  3. Java面向对象 Main函数 静态的应用 单例设计模式

     Java面向对象 Main函数 静态的应用与单例设计模式 知识概要             (1)Main函数的细解 (2)静态的应用,静态变量,静态代码块,静态函数 (3)单例设计模式 1.M ...

  4. Java静态变量的用法:伪单例

    这几天遇到一个问题,一个Service里有一个map,但是这个Service有别的继承,于是每一个Service都会创建一个map,但是这个map应该是公用的,于是就有问题了...(按结构说Servi ...

  5. Spring单例Bean和线程安全

    Spring的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢?例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框 ...

  6. Java 单例模式详解

    概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. ...

  7. 0013 Java学习笔记-面向对象-static、静态变量、静态方法、静态块、单例类

    static可以修饰哪些成员 成员变量---可以修饰 构造方法---不可以 方法---可以修饰 初始化块---可以修饰 内部类(包括接口.枚举)---可以修饰 总的来说:静态成员不能访问非静态成员 静 ...

  8. 转 java 类 单例

    转 单例概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一 ...

  9. Java单例模式深入详解

    原文地址:http://www.cnblogs.com/hxsyl/ 仅作为笔记收藏…… 一.问题引入 偶然想想到的如果把Java的构造方法弄成private,那里面的成员属性是不是只有通过stati ...

随机推荐

  1. ASP.NET MVC5 实现分页查询

    对于大量数据的查询和展示使用分页是一种不错的选择,这篇文章简要介绍下自己实现分页查询的思路. 分页需要三个变量:数据总量.每页显示的数据条数.当前页码. //数据总量 int dataCount; / ...

  2. JAVA对特殊的字符串进行html编码

    SourceURL:about:blank /** * Created by Administrator on 2016/9/22. */public class HtmlEncode { publi ...

  3. [故障公告] 13:52-14:03,访问量突增,博客web服务器CPU 100%

    13:52-14:03,由于访问量突增,博客web服务器全线CPU 100%,造成博客站点不正常访问,由此给您带来麻烦,请您谅解. 为了迎接访问量的增长给web服务器CPU带来的巨大压力,上周我们已经 ...

  4. Angular企业级开发(8)-控制器的作用域

    scope概念 scope(作用域)是视图和控制器之间的桥梁,scope本身是一个对象,有方法和属性.scope可以应用在视图和控制器上. scope简单示例 <!DOCTYPE html> ...

  5. 2017-2-18 net 输入输出语句

    控制台程序的创建,输出,输入语句,定义变量,变量赋值,值覆盖,值拼接,值打印两种数据类型,整形类型转换 知识点: 1.输出语句 Console.WriteLine("");光标换行 ...

  6. iOS基础之顺传逆传传值(delegate、block)

    写给iOS新手的福利! 在项目中经常会用到传值,根据传值的方向分为顺传(从根控制器到子控制器)和逆传(从子控制器到根控制器).在这里写了个Demo简单演示了效果,创建了两个控制器: 一个为根控制器,一 ...

  7. Javascript中的bind()函数

    今天看到公司大神的一段代码: function ReplaceProcessor() { this._dom = { btnReplace: $('#ro_btnReplace'), btnCompl ...

  8. 安全体系(二)——RSA算法详解

    本文主要讲述RSA算法使用的基本数学知识.秘钥的计算过程以及加密和解密的过程. 安全体系(零)—— 加解密算法.消息摘要.消息认证技术.数字签名与公钥证书 安全体系(一)—— DES算法详解 1.概述 ...

  9. win7下使用git

    1 安装git for window 2 安装tortoiseGit 3 生成public key 3.1 打开git bash 3.2 创建~/.ssh文件夹 mkdir ~/.ssh 3.3 配置 ...

  10. Swashbuckle Swagger组件扩展

      用Swagger有一段时间, 我的model层是一个单独的dll 但给Swagger配置的是api层dll的XML. 所以就导致了model字段的注释不能够反应到参数说明. 所以我fork了一份 ...