JDK源码——java.util.concurrent(七)
来源:互联网 发布:软件服务外包合同范本 编辑:程序博客网 时间:2024/04/30 13:13
CopyOnWriteArrayList、CopyOnWriteArraySet
这两个类都比较简单内部有一个数组和一把锁,对所有写操作加锁.每次进行写操作时都复制一个新的数组,在新数组上进行;而读则在老数组上进行,有读写分离的意思,比Vector效率高,适合都多写少的情况.
咱们看看其如何实现的
transient final ReentrantLock lock = new ReentrantLock(); private volatile transient Object[] array; public CopyOnWriteArrayList() { setArray(new Object[0]); } final void setArray(Object[] a) { array = a; }
先看看其读取方法
private static int indexOf(Object o, Object[] elements, int index, int fence) { if (o == null) { for (int i = index; i < fence; i++) if (elements[i] == null) return i; } else { for (int i = index; i < fence; i++) if (o.equals(elements[i])) return i; } return -1; } private static int lastIndexOf(Object o, Object[] elements, int index) { if (o == null) { for (int i = index; i >= 0; i--) if (elements[i] == null) return i; } else { for (int i = index; i >= 0; i--) if (o.equals(elements[i])) return i; } return -1; } public boolean contains(Object o) { Object[] elements = getArray(); return indexOf(o, elements, 0, elements.length) >= 0; } public int indexOf(Object o) { Object[] elements = getArray(); return indexOf(o, elements, 0, elements.length); } public int lastIndexOf(Object o) { Object[] elements = getArray(); return lastIndexOf(o, elements, elements.length - 1); }
这些读的方法都很简单,也没什么新意.下面看看写操作
//指定位置插入元素public E set(int index, E element) { final ReentrantLock lock = this.lock; //加锁 lock.lock(); try { //获取旧数组 Object[] elements = getArray(); //获取旧元素 E oldValue = get(elements, index); //不相等则要覆盖旧元素 if (oldValue != element) { int len = elements.length; //复制出一个新的数组 Object[] newElements = Arrays.copyOf(elements, len); //覆盖旧元素 newElements[index] = element; //适用新数组 setArray(newElements); } else { setArray(elements); } return oldValue; } finally { lock.unlock(); } } public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } } public E remove(int index) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; E oldValue = get(elements, index); int numMoved = len - index - 1; if (numMoved == 0) setArray(Arrays.copyOf(elements, len - 1)); else { Object[] newElements = new Object[len - 1]; System.arraycopy(elements, 0, newElements, 0, index); System.arraycopy(elements, index + 1, newElements, index, numMoved); setArray(newElements); } return oldValue; } finally { lock.unlock(); } }
可以看到所有的写操作都加了锁且都是操作新数组,这样能有效避免写操作之间的影响,且能避免进行写操作时读操作不准确;但这样加大了内存的消耗,因此适合读多写少的场景.
CopyOnWriteArraySet内部持有一个CopyOnWriteArrayList引用,所有操作都是基于对CopyOnWriteArrayList的操作.
ArrayBlockingQueue
首先咱们先来看看其用法:
public class ArrayBlockingQueueTest { public static void main(String[] args) { final ArrayBlockingQueue<String> queue = new ArrayBlockingQueue(3); Runnable runnable = new Runnable() { @Override public void run() { for (int i = 0; i < 3; i++) { try { queue.put("a"); TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } } }; new Thread(runnable).start(); Runnable runnable2 = new Runnable() { @Override public void run() { for (int i = 0; i < 3; i++) { try { String take = queue.take(); System.out.println(take); } catch (InterruptedException e) { e.printStackTrace(); } } } }; new Thread(runnable2).start(); }}
ArrayBlockingQueue阻塞有界队列,具有一下几个基本特点:
- 基于数组实现的阻塞有界FIFO队列
- 基于数组创建因此队列大小不能改变;当队列满时添加元素方法会被阻塞,当队列空时获取元素方法会被阻塞
- 提供公平模式、非公平模式
下面咱们来看看其源码,先看其主要属性与构造方法
//存放元素的数组 final Object[] items; //take, poll, peek or remove等方法操作的元素位置 int takeIndex; //put, offer, or add等方法操作的元素位置 int putIndex; //队列中元素数量 int count; //ReentrantLock控制并发 final ReentrantLock lock; //取出元素方法等待条件 private final Condition notEmpty; //添加元素方法等待条件 private final Condition notFull; //默认非公平锁 public ArrayBlockingQueue(int capacity) { this(capacity, false); } public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); this.items = new Object[capacity]; lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); }
下面看看其主要方法,先看add()与offer()
//add()实际上是调用offer()完成的 public boolean add(E e) { return super.add(e); } //可以看出,此方法实际上是不阻塞的 public boolean offer(E e) { //检查元素是否为Null,如果为null直接抛出异常 checkNotNull(e); //加锁 final ReentrantLock lock = this.lock; lock.lock(); try { //conut==items.length时说明队列已满 if (count == items.length) return false; else { insert(e); return true; } } finally { lock.unlock(); } } public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { //检查元素 checkNotNull(e); // long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; //响应中断 lock.lockInterruptibly(); try { //如果队列满了且等待时间小于等于0,则返回false;否则等待指定时间 while (count == items.length) { if (nanos <= 0) return false; nanos = notFull.awaitNanos(nanos); } insert(e); return true; } finally { lock.unlock(); } } //插入方法 private void insert(E x) { items[putIndex] = x; putIndex = inc(putIndex);//put元素位置 ++count; //通知lock的非空条件队列上的线程 notEmpty.signal(); } final int inc(int i) { return (++i == items.length) ? 0 : i; }
再看看put()和take()
public void put(E e) throws InterruptedException { //检查元素 checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { //如果队列满了则阻塞,等待队列不满 while (count == items.length) notFull.await(); insert(e); } finally { lock.unlock(); } } public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { //如果队列为空,则等待 while (count == 0) notEmpty.await(); return extract(); } finally { lock.unlock(); } } //获取一个元素 private E extract() { final Object[] items = this.items; E x = this.<E>cast(items[takeIndex]); items[takeIndex] = null; //改变take位置 takeIndex = inc(takeIndex); --count; //通知lock的满条件队列上的线程 notFull.signal(); return x; }
可以看到put()和take()方法是阻塞队列的真正实现方法,当队列满了时,put()方法被阻塞,直到有元素被取出,put()方法才能添加元素;当队列为空时,take()方法被阻塞直到有元素被添加,take()方法才能获取元素
再来看看poll()方法
public E poll() { final ReentrantLock lock = this.lock; lock.lock(); try { //如果队列为空则返回null;否则返回一个元素 return (count == 0) ? null : extract(); } finally { lock.unlock(); } } public E poll(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { //如果队列为null且等待时间<=0则等待指定时间,否则返回null while (count == 0) { if (nanos <= 0) return null; nanos = notEmpty.awaitNanos(nanos); } return extract(); } finally { lock.unlock(); } }
可以看到poll()方法也是非阻塞的.这个类用法、实现也都比较简单.
0 0
- JDK源码——java.util.concurrent(七)
- JDK源码——java.util.concurrent
- JDK源码——java.util.concurrent(二)
- JDK源码——java.util.concurrent(三)
- JDK源码——java.util.concurrent(四)
- JDK源码——java.util.concurrent(五)
- JDK源码——java.util.concurrent(六)
- JDK源码——java.util.concurrent(八)
- JDK源码(FutureTask)——java.util.concurrent(十)
- JDK源码阅读 java.util.concurrent—并发容器类
- Jdk源码阅读之Java.util.concurrent
- JDK源码(线程池ThreadPoolExecutor)——java.util.concurrent(九)
- JDK源码阅读 java.util.concurrent.Executors相关分析
- java.util.concurrent解析——FutureTask源码解析
- java.util.concurrent解析——ThreadPoolExecutor源码解析
- JDK源码——java.util
- java.util.concurrent 源码阅读
- java.util.concurrent——TimeUnit
- Java 并发编程知识点(三)
- java中的IO操作(一)【个人笔记】
- 油田问题
- java中的IO操作(二)【个人笔记】
- 响应式 web 设计(一) 之 响应式设计入门
- JDK源码——java.util.concurrent(七)
- 南阳oj题目20吝啬的国度 菜鸟的进阶之路
- Github全面详解-17创建组织代码仓库
- Playrix Codescapes Cup (Codeforces Round #413, rated, Div. 1 + Div. 2) C. Fountains(树状数组)
- 多关键字排序-oj考试排名10.1.3
- C#使用委托实现主窗体向子窗体发送消息
- 优先级队列
- POJ 1124 Oh, Those Achin' Feet 笔记
- 快速排序 C++