消息队列

应用场景:

当你不需要立即获得结果,但是并发量又不能无限大的时候,差不多就是你需要使用消息队列的时候。

 

原理:

消息队列,首先是个队列。队列的操作有入队出队。先进先出

也就是你有一个程序在产生内容然后入队(生产者), 另一个程序读取内容,内容出队(消费者)

 

案例:

 

  • Zookeeper注册中心,提出负载均衡和地址查找服务;(ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件)
  • 日志收集客户端,用于采集应用系统的日志,并将数据推送到kafka队列;
  • Kafka集群:接收,路由,存储,转发等消息处理;

 

 讲消息队列就不得不提JMS 。

JMS(Java Message Service,java消息服务)API是一个消息服务的标准/规范,允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。

在JMS标准中,有两种消息模型P2P(Point to Point),Publish/Subscribe(Pub/Sub)。

P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。

 

 

包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

 

JNDI:Java命名和目录接口,是一种标准的Java命名系统接口。可以在网络上查找和访问服务。通过指定一个资源名称,该名称对应于数据库或命名服务中的一个记录,同时返回资源连接建立所必须的信息。

JNDI在JMS中起到查找和访问发送目标或消息来源的作用。

 

常用消息队列

一般商用的容器,比如WebLogic,JBoss,都支持JMS标准,开发上很方便。

但免费的比如Tomcat,Jetty等则需要使用第三方的消息中间件

本部分内容介绍常用的消息中间件(Active MQ,Rabbit MQ,Zero MQ,Kafka)以及他们的特点。

 

JAVA中的实现:

Queue是什么

队列,是一种数据结构。除了优先级队列和LIFO队列外,队列都是以FIFO(先进先出)的方式对各个元素进行排序的。无论使用哪种排序方式,队列的头都是调用remove()或poll()移除元素的。在FIFO队列中,所有新元素都插入队列的末尾。

1、BlockingQueue概述

只讲BlockingQueue,因为BlockingQueue是Queue中的一个重点,并且通过BlockingQueue我们再次加深对于生产者/消费者模型的理解。其他的Queue都不难,通过查看JDK API和简单阅读源码完全可以理解他们的作用。

BlockingQueue,顾名思义,阻塞队列。BlockingQueue是在java.util.concurrent下的,因此不难理解,BlockingQueue是为了解决多线程中数据高效安全传输而提出的。

阻塞队列所谓的"阻塞",指的是某些情况下线程会挂起(即阻塞),一旦条件满足,被挂起的线程又会自动唤醒。使用BlockingQueue,不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,这些内容BlockingQueue都已经做好了

BlockingQueue中特有的方法:

(1)void put(E e) throws InterruptedException

把e添加进BlockingQueue中,如果BlockingQueue中没有空间,则调用线程被阻塞,进入等待状态,直到BlockingQueue中有空间再继续

(2)void take() throws InterruptedException

取走BlockingQueue里面排在首位的对象,如果BlockingQueue为空,则调用线程被阻塞,进入等待状态,直到BlockingQueue有新的数据被加入

(3)int drainTo(Collection<? super E> c, int maxElements)

一次性取走BlockingQueue中的数据到c中,可以指定取的个数。通过该方法可以提升获取数据效率,不需要多次分批加锁或释放锁

 

实现类:

ArrayBlockingQueue,其构造函数必须带一个int参数来指明其大小

LinkedBlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定

PriorityBlockingQueue,其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序

 

 

2、ArrayBlockingQueue

基于数组的阻塞队列,必须指定队列大小

ArrayBlockingQueue中只有一个ReentrantLock对象,这意味着生产者和消费者无法并行运行(见下面的代码)。

另 外,创建ArrayBlockingQueue时,可以指定ReentrantLock是否为公平锁,默认采用非公平锁。

LinkedBlockingQueue
由于LinkedBlockingQueue实现是线程安全的,实现了先进先出等特性,是作为生产者消费者的首选,LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE

 

 http://blog.csdn.net/zmx729618/article/details/52980158

http://blog.csdn.net/kangbin825/article/details/53966618

 

 

 

非阻塞队列:

ConcurrentLinkedQueue,它是一个无锁的并发线程安全的队列

ConcurrentLinkedQueue有两个volatile的线程共享变量:head,tail。要保证这个队列的线程安全就是保证对这两个Node的引用的访问(更新,查看)的原子性和可见性,

由于volatile本身能够保证可见性,所以就是对其修改的原子性要被保证。

 

在使用ConcurrentLinkedQueue时要注意,如果直接使用它提供的函数,比如add或者poll方法,这样我们自己不需要做任何同步。 
但如果是非原子操作,比如: 

Java代码 复制代码 收藏代码
  1. if(!queue.isEmpty()) {
  2. queue.poll(obj);
  3. }

我们很难保证,在调用了isEmpty()之后,poll()之前,这个queue没有被其他线程修改。所以对于这种情况,我们还是需要自己同步: 

Java代码 复制代码 收藏代码
  1. synchronized(queue) {
  2. if(!queue.isEmpty()) {
  3. queue.poll(obj);
  4. }
  5. }

注:这种需要进行自己同步的情况要视情况而定,不是任何情况下都需要这样做。 

ConcurrentLinkedQueue的size()是要遍历一遍集合的,所以尽量要避免用size而改用isEmpty(),以免性能过慢。 

 

posted @ 2016-09-26 16:11  malcome  阅读(159)  评论(0)    收藏  举报