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阻塞有界队列,具有一下几个基本特点:

  1. 基于数组实现的阻塞有界FIFO队列
  2. 基于数组创建因此队列大小不能改变;当队列满时添加元素方法会被阻塞,当队列空时获取元素方法会被阻塞
  3. 提供公平模式、非公平模式

下面咱们来看看其源码,先看其主要属性与构造方法

 //存放元素的数组 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
原创粉丝点击