java并发编程之美-阅读记录5

java并发包中的并发List

5.1CopeOnWriteArrayList

  并发包中的并发List只有CopyOnWriteArrayList,该类是一个线程安全的arraylist,对其进行的修改操作都是在底层的一个复制数组上进行的,也就是使用了写时复制策略。

  该类的结构:

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8673264195747942595L;
    // 可重入的独占锁,用来保证对arraylist的修改操作,同一时间只有一个线程
    final transient ReentrantLock lock = new ReentrantLock();
    // 存放对象的底层数组 内存可见性
    private transient volatile Object[] array;
    // 基于硬件的原子操作了Unsafe
    private static final sun.misc.Unsafe UNSAFE;
    // 锁的偏移量
    private static final long lockOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = CopyOnWriteArrayList.class;
            lockOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("lock"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

问题:

  何时初始化list,初始化list的大小是多少,list是有限大小吗?    copyonwriteArraylist是无界数组

  如何保证线程安全,比如多个线程进行读写时如何保证是线程安全的?

  如何保证迭代器遍历list时的数据一致性?

5.2源码分析

1、初始化

  构造函数:

    // 空的list
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }
    // 入参为Collection类型的集合,该构造会将集合中的元素复制到list中
    public CopyOnWriteArrayList(Collection<? extends E> c) {
        Object[] elements;
        if (c.getClass() == CopyOnWriteArrayList.class)
            elements = ((CopyOnWriteArrayList<?>)c).getArray();
        else {
            elements = c.toArray();
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elements.getClass() != Object[].class)
                elements = Arrays.copyOf(elements, elements.length, Object[].class);
        }
        setArray(elements);
    }
   // 入参为泛型数组,将数组复制到list的底层数组中
    public CopyOnWriteArrayList(E[] toCopyIn) {
        setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
    }

2、添加元素add方法

    // 添加元素,在list的末尾添加
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;        // 获取独占锁
        lock.lock();
        try {           // 获取copyOnWriteArrayList底层数组
            Object[] elements = getArray();
            int len = elements.length;        // 复制一份新的数组,比原数组大一,所以CopyOnWriteArraylist是一个无界数组
            Object[] newElements = Arrays.copyOf(elements, len + 1);            // 将要添加的元素放到新数组的最后位置
            newElements[len] = e;            // 使用新数组替换原来的数组
            setArray(newElements);
            return true;
        } finally {            // 操作完毕后,释放独占锁
            lock.unlock();
        }
    }

3、获取元素,此时就会产生写时复制的弱一致性问题:当线程a获取到地层数组后,但是没有执行get(Object[] a,int index)方法,此时线程b操作该集合,删除了一个元素(删除的是复制出的新数组中的数据,同时会使用新数组覆盖旧数组),name线程a继续获取执行位置的数据,此时底层数组仍然是之前没有删除数据的数组,这样就产生了弱一致性问题。

    // 直接获取底层数组指定索引位置的数据
    private E get(Object[] a, int index) {
        return (E) a[index];
    }

    // 获取指定索引出的值    getArray方法返回底层数组
    public E get(int index) {
        return get(getArray(), index);
    }

4、修改指定位置的元素

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 {
                // Not quite a no-op; ensures volatile write semantics
                // 旧数组重新覆盖旧数组--> 这一步虽然没有改变数组,但是为了保证volatitle语义,仍然会重新设置一次
                setArray(elements);
            }
            return oldValue;
        } finally {
            // 释放锁
            lock.unlock();
        }
    } 

5、删除元素

    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;
            // 要移动的元素为0,则代表删除的是最后一个元素
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                // 要删除的不是最后一个,则新建一个len-1长度的数组
                Object[] newElements = new Object[len - 1];
                // 同时以要删除的索引index为分界线,复制0-index,index+1 - len的元素
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                setArray(newElements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

6、弱一致性的迭代器

  所谓弱一致性是指:返回迭代器后,其他线程对list的操作(增删改)对迭代器是不可见的。

    public Iterator<E> iterator() {
        return new COWIterator<E>(getArray(), 0);
    }

    // 可以看到迭代器中的方法,不能增删改,相应的方法会抛出异常
    static final class COWIterator<E> implements ListIterator<E> {
        /** Snapshot of the array */
        private final Object[] snapshot;
        /** Index of element to be returned by subsequent call to next.  */
        private int cursor;

        private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;
        }

        public boolean hasNext() {
            return cursor < snapshot.length;
        }

        public boolean hasPrevious() {
            return cursor > 0;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            if (! hasNext())
                throw new NoSuchElementException();
            return (E) snapshot[cursor++];
        }

        @SuppressWarnings("unchecked")
        public E previous() {
            if (! hasPrevious())
                throw new NoSuchElementException();
            return (E) snapshot[--cursor];
        }

        public int nextIndex() {
            return cursor;
        }

        public int previousIndex() {
            return cursor-1;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

        public void set(E e) {
            throw new UnsupportedOperationException();
        }

        public void add(E e) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            Object[] elements = snapshot;
            final int size = elements.length;
            for (int i = cursor; i < size; i++) {
                @SuppressWarnings("unchecked") E e = (E) elements[i];
                action.accept(e);
            }
            cursor = size;
        }
    }

弱一致性:

package com.nxz.blog.otherTest;

import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

public class TestThread003 {

    private static CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();

    /**
     * 测试CopyOnWriteArrayList的弱一致性问题
     * 如果输出结果是:test1 test2 test3 test4  则说明该类具有弱一致性    
     */
    public static void main(String[] args) throws InterruptedException {

        copyOnWriteArrayList.add("test1");
        copyOnWriteArrayList.add("test2");
        copyOnWriteArrayList.add("test3");
        copyOnWriteArrayList.add("test4");

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                copyOnWriteArrayList.add("runnabl1");
                copyOnWriteArrayList.add("runnabl2");
                copyOnWriteArrayList.add("runnabl3");
            }
        });

        Iterator<String> iterator = copyOnWriteArrayList.iterator();

        // 等待线程执行完毕
        t.join();

        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}
结果:
test1
test2
test3
test4

原文地址:https://www.cnblogs.com/nxzblogs/p/11332231.html

时间: 2024-07-30 11:07:07

java并发编程之美-阅读记录5的相关文章

java并发编程之美-阅读记录1

1.1什么是线程? 在理解线程之前先要明白什么是进程,因为线程是进程中的一个实体.(线程是不会独立存在的) 进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程中的一个执行路径,一个进程中至少会有一个线程,进程中的多个线程共享进程的资源. 线程:是cpu分配的基本单位. 由上图可看出,一个进程中会有多个线程,多个线程共享堆和方法区,但是每一个线程都会有自己的栈和程序计数器. 为什么要将栈和程序计数器设置为线程私有的呢? 前边说线程是cpu执行的基本单位,而cp

java并发编程之美-阅读记录11

java并发编程实践 11.1ArrayBlockingQueue的使用 有关logback异步日志打印中的ArrayBlockingQueue的使用 1.异步日志打印模型概述 在高并发.高流量并且响应时间要求比较小的系统中同步打印日志在性能上已经满足不了了,这是以因为打印本身是需要写磁盘的,写磁盘操作会暂时阻塞调用打印日志的业务系统,这会造成调用线程的响应时间增加.    ----- >>> 异步日志打印,是将打印日志任务放入一个队列后就返回,然后使用一个线程专门从队列中获取日志任务,

java并发编程之美-阅读记录2

2.1什么是多线程并发编程 并发:是指在同一时间段内,多个任务同时在执行,并且执行没有结束(同一时间段又包括多个单位时间,也就是说一个cpu执行多个任务) 并行:是指在单位时间内多个任务在同时执行(也就是多个cpu同时执行任务) 而在多线程编程实践中,线程的个数一般是多于cpu的个数的 2.2为什么要多线程并发编程 多个cpu同时执行多个任务,减少了线程上下文切换的开销 2.3线程安全问题 共享资源:就是说该资源可以被多个线程持有,或者说能够被多个线程访问. 对共享资源的修改会造成线程安全问题.

《Java并发编程之美》(翟陆续著)高清pdf

<Java并发编程之美> 阿里巴巴技术专家力作,用代码说话.用实例验证,并发编程没有这么难!<Java并发编程的艺术>*作者方腾飞老师好评推荐! ? 百度网盘链接: https://pan.baidu.com/s/12oEEeDEO_YofImkpQA1bLA 提取码: pmkh  内容简介  · · · · · · 并发编程相比 Java 中其他知识点的学习门槛较高,从而导致很多人望而却步.但无论是职场面试,还是高并发/ 高流量系统的实现,却都离不开并发编程,于是能够真正掌握并发

Java并发编程之美之并发编程线程基础

什么是线程 进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程至少有一个线程,进程的多个线程共享进程的资源. java启动main函数其实就是启动了一个JVM的进程,而main函数所在的线程就是这个进程的一个线程,也称主线程. 进程和线程关系 一个进程有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程有自己的程序计数器和栈区域. 程序计数器是一块内存区域,用来记录线程当前要执行的指令地址.如果执行的是native方法,那么pc计

[Java并发编程之美]第2章 并发编程的其他基础知识 补充知识

synchronized与volatile关键字 一. synchronized synchronized是Java语法中的一个内置锁的实现.synchronized关键字解决了代码块或者方法上的同步问题,同一时间,只有一个线程能够通过并执行.保证线程安全:内存可见性和原子性提供了并发场景的一个共享资源访问的解决方案. 当我们说synchronized锁住的是一个JVM对象时,真正发挥作用的是对象头上所指向的monitor对象(监视器机制:Java锁的底层实现). synchronized有两种

[Java并发编程之美]第1章 线程基础(待更新)

第1章 线程 线程与进程 进程是操作系统资源分配和调度的基本单位,但cpu资源是分配到线程的,也就是线程是CPU分配的基本单位. 线程自己的栈资源中,存放的局部变量是线程私有的,其他线程无法访问,除此之外栈还存线程的调用栈帧. 线程创建 三种方式:实现Runnable接口的run方法:继承Thread类并重写run方法:使用FutureTask方式. 线程等待与通知 1 wait() 线程先要事先获得共享变量上的监视器锁,然后当一个线程调用一个共享变量的wait()方法,该线程会被阻塞挂起,并且

【java并发编程艺术学习】(三)第二章 java并发机制的底层实现原理 学习记录(一) volatile

章节介绍 这一章节主要学习java并发机制的底层实现原理.主要学习volatile.synchronized和原子操作的实现原理.Java中的大部分容器和框架都依赖于此. Java代码 ==经过编译==>Java字节码 ==通过类加载器==>JVM(jvm执行字节码)==转化为汇编指令==>CPU上执行. Java中使用的并发机制依赖于JVM的实现和CPU的指令. volatile初探 volatile是是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性.可见性

java并发编程6.取消与关闭

如果外部代码能在某个操作正常完成之前将其置入"完成"状态,那么这个操作就可以称为可取消的. Java没有提供任何机制来安全地终止线程.但它提供了中断,这是一种协作机制,能够使一个线程终止另一个线程的当前工作. 其中一种协作机制能设置某个"已请求取消"的标志,而任务将定期地查看该标志,如果设置了这个标志,那么任务将提前结束. 自定义取消机制 /** * 素数生成器 */ private class PrimeGenerator implements Runnable{