最近要使用alibaba的rocket mq(我们公司对其进行了封装,使其运行在dotNet平台上,Java还是和原生的差不多,涉及公司的内容本文不会提及),其中 在生产者组这一块,建议是用单例模式的。但是其中又建议一个组(group)使用一个实例,这样仅仅单例模式就不行了,所以要进行改动,我们的目标就是“一个group使用一个单例”。

  其实简单点,多封装几个不同的单例类就行了,一个组用一个类。但是这显然不是一个好主意,于是我们来考虑用另一种方式。

  首先要将 group 这个概念抽出来,它是变量,接下来封装不变的代码。

  我们先看看代码是什么样的:

  1. /**
  2. * TurboMQ 消息生产者管理器
  3. */
  4. public class MqProducer {
  5.  
  6. private DefaultMQProducer currentMQProducer;
  7.  
  8. private static Map<String, MqProducer> producerMap = new ConcurrentHashMap<>(3);
  9. private static final Object lock = new Object();
  10.  
  11. private MqProducer(String group) throws MQClientException {
  12. if (!Validator.isNotNullAndVisible(group)) {
  13. throw new NullPointerException("Group名称不能为空!");
  14. }
  15.  
  16. currentMQProducer = new DefaultMQProducer(group);
  17. currentMQProducer.setNamesrvAddr(“1.1.1.1”);
  18. currentMQProducer.start();
  19. }
  20.  
  21. public static MqProducer instance(String group) throws MQClientException {
  22. if (!Validator.isNotNullAndVisible(group)) {
  23. throw new NullPointerException("Group名称不能为空!");
  24. }
  25.  
  26. if (producerMap.get(group) == null) {
  27. synchronized (lock) {
  28. if (producerMap.get(group) == null) {
  29. producerMap.put(group, new MqProducer(group));
  30. }
  31. }
  32. }
  33. return producerMap.get(group);
  34. }
  35.  
  36. public SendResult send(String topic, String tag, String body) throws UnsupportedEncodingException, InterruptedException, RemotingException, MQClientException, MQBrokerException {
  37. if (!Validator.isNotNullAndVisible(topic, tag, body)) {
  38. throw new NullPointerException("请检查参数是否为空,topic,tag,body");
  39. }
  40. Message message = new Message(topic, tag, body.getBytes("UTF-8"));
  41. return currentMQProducer.send(message);
  42. }
  43.  
  44. public static void shutdownAll() {
  45. producerMap.forEach((key, value) -> {
  46. value.shutdown();
  47. });
  48. }
  49.  
  50. public void shutdown() {
  51. currentMQProducer.shutdown();
  52. }
  53.  
  54. }

  我们的解决思路,就是使用 Map 让 group 和实例一一对应起来。

  这些代码中你可能需要注意的点是:

    1 线程安全的 ConcurrentHashMap 以及要设置初始容量

  1. private static Map<String, MqProducer> producerMap = new ConcurrentHashMap<>(3);

    2 instance方法中的两层 if 判断

       在 synchronized(lock)锁住之前可能有多个线程了解到当前组是null,都去请求锁,当第一个线程new了新生产者之后,下一个进程进来就不会再new一个新的生产者了。

  1. public static MqProducer instance(String group) throws MQClientException {
  2. if (producerMap.get(group) == null) {
  3. synchronized (lock) {
  4. if (producerMap.get(group) == null) {
  5. producerMap.put(group, new MqProducer(group));
  6. }
  7. }
  8. }
  9. return producerMap.get(group);
  10. }

  题外话:

    为什么要抛异常?

    因为此处是通用代码,通用代码不应处理业务逻辑,而且不该隐蔽错误的发生,要让业务逻辑去确保参数没问题。

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. delete和delete[]

    c++中对new申请的内存的释放方式有delete和delete[两种方式,到底这两者有什么区别呢? 1.我们通常从教科书上看到这样的说明:delete 释放new分配的单个对象指针指向的内存dele ...

  2. (转)JAVA的整型与字符串相互转换

    JAVA的整型与字符串相互转换1如何将字串 String 转换成整数 int? A. 有两个方法: 1). int i = Integer.parseInt([String]); 或         ...

  3. 使用 position:sticky 实现粘性布局

    如果问,CSS 中 position 属性的取值有几个?大部分人的回答是,大概是下面这几个吧? { position: static; position: relative; position: ab ...

  4. DataReader的用法程序简析

    // 2015/07/05 using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

  5. jsp内置对象的方法

    JSP内置对象的方法:out:out.print();request:request对象主要用于出列客户端请求.   常用方法:    String getParameter(String name) ...

  6. SQL中的join连接查询

      inner join(交集 ,自然连接, 简写成join)   是最普通的连接查询,相当于早期根据where条件连接的查询     outer join(并集或部分并集,左表 + 右表)   le ...

  7. 如何垂直居中<img>?

    方法1: 父元素设置height=line-height,子元素设置vertical-align:middle; 方法2: 父元素display:table-cell;vertical-align:m ...

  8. Javascript把数据从一个页面的层传递到另一个页面层里面

    背景:昨天头脑发热投了某一家国企的计算机类岗位(说是有前端岗位),通过找同学内推,虽然也笔试了一大堆题目(行测题,计算机网络,http协议,英译汉,古诗文默写,自己把品质排序并且进行200字以上的阐述 ...

  9. java算法 蓝桥杯(题+答案) 方格填数

    6.方格填数  (结果填空) 如下的10个格子 (如果显示有问题,也可以参看[图1.jpg]) 填入0~9的数字.要求:连续的两个数字不能相邻.(左右.上下.对角都算相邻) 一共有多少种可能的填数方案 ...

  10. JS事件监听器 addEventListener

    一:例如:给id为mydiv1的div元素添加click事件监听器document.getElementById("mydiv1").addEventListener(" ...