《琼明神女录》漫画网免费观看:开启你的奇幻冒险之旅!

核心内容摘要

伊人风韵,恰是人间成年人的浪漫与生活美学
刘亦菲“黑金刚”角色大赏:从绝世仙女到霸气女王,谁是你心中的最爱?

视觉洪流中的极致诱惑:探寻“黄色视频在线看免费高清无广告”的终极观影哲学

Java 的并发集合容器提供了在多线程环境中高效访问和操作的数据结构。

这些容器通过内部的同步机制实现了线程安全使得开发者无需显式同步代码就能在并发环境下安全使用比如说ConcurrentHashMap、阻塞队列和 CopyOnWrite 容器等。

java.util 包下提供了一些容器类集合框架其中 Vector 和 Hashtable 是线程安全的但实现方式比较粗暴通过在方法上加「sychronized」关键字实现。

但即便是 Vector 这样线程安全的类在应对多线程的复合操作时也需要在客户端继续加锁以保证原子性。

来看下面的例子public class TestVector { private VectorString vector; //方法一 public Object getLast(Vector vector) { int lastIndex vector.size() - 1; return vector.get(lastIndex); } //方法二 public void deleteLast(Vector vector) { int lastIndex vector.size() - 1; vector.remove(lastIndex); } //方法三 public Object getLastSysnchronized(Vector vector) { synchronized(vector){ int lastIndex vector.size() - 1; return vector.get(lastIndex); } } //方法四 public void deleteLastSysnchronized(Vector vector) { synchronized (vector){ int lastIndex vector.size() - 1; vector.remove(lastIndex); } } }如果方法一和方法二是一个组合的话那么当方法一获取到了vector的 size 之后方法二已经执行完毕这样就会导致程序出现错误。

如果方法三与方法四组合的话就还需在内部加锁来保证vector上的原子性操作。

于是并发容器就应用而生了它们是线程安全的可以在多线程环境下高效地访问和操作数据而不需要额外的同步措施。

并发容器类整体架构如下图所示并发 MapConcurrentMap 接口ConcurrentMap 接口继承了 Map 接口在 Map 接口的基础上又定义了四个方法public interface ConcurrentMapK, V extends MapK, V { //插入元素 V putIfAbsent(K key, V value); //移除元素 boolean remove(Object key, Object value); //替换元素 boolean replace(K key, V oldValue, V newValue); //替换元素 V replace(K key, V value); }putIfAbsent与原有 put 方法不同的是putIfAbsent 如果插入的 key 相同则不替换原有的 value 值remove与原有 remove 方法不同的是新 remove 方法中增加了对 value 的判断如果要删除的 key-value 不能与 Map 中原有的 key-value 对应上则不会删除该元素;replace(K,V,V)增加了对 value 值的判断如果 key-oldValue 能与 Map 中原有的 key-value 对应上才进行替换操作replace(K,V)与上面的 replace 不同的是此 replace 不会对 Map 中原有的 key-value 进行比较如果 key 存在则直接替换ConcurrentHashMapConcurrentHashMap 同 HashMap 一样也是基于散列表的 map但是它提供了一种与 Hashtable 完全不同的加锁策略提供了更高效的并发性和伸缩性。

ConcurrentSkipListMapConcurrentNavigableMap 接口继承了 NavigableMap 接口这个接口提供了针对给定搜索目标返回最接近匹配项的导航方法。

ConcurrentNavigableMap 接口的主要实现类是 ConcurrentSkipListMap 类。

从名字上来看它的底层使用的是跳表SkipList。

跳表是一种”空间换时间“的数据结构可以使用 CAS 来保证并发安全性。

与 ConcurrentHashMap 的读密集操作相比ConcurrentSkipListMap 的读和写操作的性能相对较低。

这是由其数据结构导致的因为跳表的插入和删除需要更复杂的指针操作。

然而ConcurrentSkipListMap 提供了有序性这是 ConcurrentHashMap 所没有的。

ConcurrentSkipListMap 适用于需要线程安全的同时又需要元素有序的场合。

如果不需要有序ConcurrentHashMap 可能是更好的选择因为它通常具有更高的性能。

并发 QueueJDK 并没有提供线程安全的 List 类因为对 List 来说很难去开发一个通用并且没有并发瓶颈的线程安全的 List。

因为即使简单的读操作比如contains()也需要再搜索的时候锁住整个 list。

所以退一步JDK 提供了队列和双端队列的线程安全类ConcurrentLinkedQueue 和 ConcurrentLinkedDeque。

因为队列相对于 List 来说有更多的限制。

这两个类是使用 CAS 来实现线程安全的。

并发 SetConcurrentSkipListSet 是线程安全的有序集合。

底层是使用 ConcurrentSkipListMap 来实现。

谷歌的 Guava实现了一个线程安全的 ConcurrentHashSetSetString s Sets.newConcurrentHashSet();阻塞队列我们假设一种场景生产者一直生产资源消费者一直消费资源资源存储在一个缓冲池中生产者将生产的资源存进缓冲池中消费者从缓冲池中拿到资源进行消费这就是大名鼎鼎的生产者-消费者模式。

该模式能够简化开发过程一方面消除了生产者类与消费者类之间的代码依赖性另一方面将生产数据的过程与使用数据的过程解耦简化负载。

我们自己 coding 实现这个模式的时候因为需要让多个线程操作共享变量即资源所以很容易引发线程安全问题造成重复消费和死锁尤其是生产者和消费者存在多个的情况。

另外当缓冲池空了我们需要阻塞消费者唤醒生产者当缓冲池满了我们需要阻塞生产者唤醒消费者这些个等待-唤醒逻辑都需要自己实现。

这么容易出错的事情JDK 当然帮我们做啦这就是阻塞队列BlockingQueue你只管往里面存、取就行而不用担心多线程环境下存、取共享变量的线程安全问题。

BlockingQueue 是 Java util.concurrent 包下重要的数据结构区别于普通的队列BlockingQueue 提供了线程安全的队列访问方式并发包下很多高级同步类的实现都是基于 BlockingQueue 实现的。

BlockingQueue 一般用于生产者-消费者模式生产者是往队列里添加元素的线程消费者是从队列里拿元素的线程。

BlockingQueue 就是存放元素的容器。

BlockingQueue 的操作方法阻塞队列提供了四组不同的方法用于插入、移除、检查元素方法\处理方式抛出异常返回特殊值一直阻塞超时退出插入方法add(e)offer(e)put(e)offer(e,time,unit)移除方法remove()poll()take()poll(time,unit)检查方法element()peek()--抛出异常如果操作无法立即执行会抛异常。

当阻塞队列满时候再往队列里插入元素会抛出IllegalStateException(“Queue full”)异常。

当队列为空时从队列里获取元素时会抛出 NoSuchElementException 异常 。

返回特殊值如果操作无法立即执行会返回一个特殊值通常是 true / false。

一直阻塞如果操作无法立即执行则一直阻塞或者响应中断。

超时退出如果操作无法立即执行该方法调用将会发生阻塞直到能够执行但等待时间不会超过给定值。

返回一个特定值以告知该操作是否成功通常是 true / false。

注意不能往阻塞队列中插入 null会抛出空指针异常。

可以访问阻塞队列中的任意元素调用remove(o)可以将队列之中的特定对象移除但并不高效尽量避免使用。

BlockingQueue 的实现类ArrayBlockingQueue由数组结构组成的有界阻塞队列。

内部结构是数组具有数组的特性。

public ArrayBlockingQueue(int capacity, boolean fair){ //..省略代码 }可以初始化队列大小一旦初始化将不能改变。

构造方法中的 fair 表示控制对象的内部锁是否采用公平锁默认是非公平锁。

LinkedBlockingQueue由链表结构组成的有界阻塞队列。

内部结构是链表具有链表的特性。

默认队列的大小是Integer.MAX_VALUE也可以指定大小。

此队列按照先进先出的原则对元素进行排序。

DelayQueue该队列中的元素只有当其指定的延迟时间到了才能够从队列中获取到该元素。

注入其中的元素必须实现java.util.concurrent.Delayed接口。

DelayQueue 是一个没有大小限制的队列因此往队列中插入数据的操作生产者永远不会被阻塞而只有获取数据的操作消费者才会被阻塞。

PriorityBlockingQueue基于优先级的无界阻塞队列优先级的判断通过构造函数传入的 Compator 对象来决定内部控制线程同步的锁采用的是非公平锁。

public PriorityBlockingQueue(int initialCapacity, Comparator? super E comparator) { this.lock new ReentrantLock(); //默认构造方法-非公平锁 ...//其余代码略 }SynchronousQueue这个队列比较特殊没有任何内部容量甚至连一个队列的容量都没有。

并且每个 put 必须等待一个 take反之亦然。

需要区别容量为 1 的 ArrayBlockingQueue、LinkedBlockingQueue。

以下方法的返回值可以帮助理解这个队列iterator()永远返回空因为里面没有东西peek()永远返回 nullput()往 queue 放进去一个 element 以后就一直 wait 直到有其他 thread 进来把这个 element 取走。

offer()往 queue 里放一个 element 后立即返回如果碰巧这个 element 被另一个 thread 取走了offer 方法返回 true认为 offer 成功否则返回 false。

take()取出并且 remove 掉 queue 里的 element取不到东西他会一直等。

poll()取出并且 remove 掉 queue 里的 element只有到碰巧另外一个线程正在往 queue 里 offer 数据或者 put 数据的时候该方法才会取到东西。

否则立即返回 null。

isEmpty()永远返回 trueremove()removeAll()永远返回 false注意PriorityBlockingQueue不会阻塞数据生产者因为队列是无界的而只会在没有可消费的数据时阻塞数据的消费者。

因此使用的时候要特别注意生产者生产数据的速度绝对不能快于消费者消费数据的速度否则时间一长会最终耗尽所有的可用堆内存空间。

对于使用默认大小的LinkedBlockingQueue也是一样的。

CopyOnWrite 容器在聊 CopyOnWrite 容器之前我们先来谈谈什么是 CopyOnWrite 机制CopyOnWrite 是计算机设计领域的一种优化策略也是一种在并发场景下常用的设计思想——写入时复制。

什么是写入时复制呢就是当有多个调用者同时去请求一个资源数据的时候有一个调用者出于某些原因需要对当前的数据源进行修改这个时候系统将会复制一个当前数据源的副本给调用者修改。

CopyOnWrite 容器即写时复制的容器当我们往一个容器中添加元素的时候不直接往容器中添加而是将当前容器进行 copy复制出来一个新的容器然后向新容器中添加我们需要的元素最后将原容器的引用指向新容器。

这样做的好处在于我们可以在并发的场景下对容器进行读操作而不需要加锁从而达到读写分离的目的。

从 JDK

5 开始 Java 并发包里提供了两个使用 CopyOnWrite 机制实现的并发容器分别是 CopyOnWriteArrayList 和 CopyOnWriteArraySet。

小结本文主要介绍了并发包中的三个重要的容器类Map、阻塞队列和 CopyOnWrite 容器Map 用于存储键值对阻塞队列用于生产者-消费者模型而 CopyOnWrite 容器用于“读多写少”的并发场景。

9.1樱花-9.1樱花应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123