科技报道|一次性搞清楚,Java并发编程在各主流框架中的应用,保证看懂( 七 )


  • LinkedBlockingQueue
基于链表的阻塞队列 , 同 ArrayListBlockingQueue 类似 , 其内部也维持着一个数据缓冲队列(该队列由一个链表构成) , 当生产者往队列中放入一个数据时 , 队列会从生产者手中获取数据 , 并缓存在队列内部 , 而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue 可以通过构造函数指定该值) , 才会阻塞生产者队列 , 直到消费者从队列中消费掉一份数据 , 生产者线程会被唤醒 , 反之对于消费者这端的处理也基于同样的原理 。 而 LinkedBlockingQueue 之所以能够高效的处理并发数据 , 还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步 , 这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据 , 以此来提高整个队列的并发性能 。 需要注意的是 , 如果构造一个 LinkedBlockingQueue 对象 , 而没有指定其容量大小 , LinkedBlockingQueue 会默认一个类似无限大小的容量(Integer.MAX_VALUE) , 这样的话 , 如果生产者的速度一旦大于消费者的速度 , 也许还没有等到队列满阻塞产生 , 系统内存就有可能已被消耗殆尽了 。
  • PriorityBlockingQueue
基于优先级的阻塞队列(优先级的判断通过构造函数传入的 Compator 对象来决定) , 但需要注意的是 PriorityBlockingQueue 并不会阻塞数据生产者 , 而只会在没有可消费的数据时 , 阻塞数据的消费者 。 因此使用的时候要特别注意 , 生产者生产数据的速度绝对不能快于消费者消费数据的速度 , 否则时间一长 , 会最终耗尽所有的可用堆内存空间 。 在实现 PriorityBlockingQueue 时 , 内部控制线程同步的锁采用的是公平锁 。
CAS 指令和原子类(应用比较多的就是计数器)互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能的额外损耗 , 因此这种同步被称为阻塞同步 , 它属于一种悲观的并发策略 , 我们称之为悲观锁 。 随着硬件和操作系统指令集的发展和优化 , 产生了非阻塞同步 , 被称为乐观锁 。 简单地说 , 就是先进行操作 , 操作完成之后再判断操作是否成功 , 是否有并发问题 , 如果有则进行失败补偿 , 如果没有就算操作成功 , 这样就从根本上避免了同步锁的弊端 。
目前 , 在 Java 中应用最广泛的非阻塞同步就是 CAS 。 从 JDK1.5 以后 , 可以使用 CAS 操作 , 该操作由 sun.misc.Unsafe 类里的 compareAndSwapInt() 和 compareAndSwapLong() 等方法实现 。 通常情况下 sun.misc.Unsafe 类 对于开发者是不可见的 , 因此 , JDK 提供了很多 CAS 包装类 简化开发者的使用 , 如 AtomicInteger 。 使用 Java 自带的 Atomic 原子类 , 可以避免同步锁带来的并发访问性能降低的问题 , 减少犯错的机会 。
全文完!
作者:yanglbme
转自自:


推荐阅读