本站提倡有节制游戏,合理安排游戏时间,注意劳逸结合。

【examstack 源码】【小霸王php源码】【手机app源码查询】synchronizedlist 源码

2024-11-26 19:13:08 来源:知识 分类:知识

1.arraylist为什么线程不安全
2.线程安全的list之synchronizedList和CopyOnWriteArrayList
3.java中Arraylist是源码干什么的?怎么用?
4.fail-fast 和 fail-safe
5.关于 List 的线程不安全
6.LinkedList(详细讲解)

synchronizedlist 源码

arraylist为什么线程不安全

       é¦–先说一下什么是线程不安全:线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。 如图,List接口下面有两个实现,一个是ArrayList,另外一个是vector。 从源码的角度来看,因为Vector的方法前加了,synchronized 关键字,也就是同步的意思,sun公司希望Vector是线程安全的,而希望arraylist是高效的,缺点就是另外的优点。 说下原理(百度的,很好理解): 一个 ArrayList ,在添加一个元素的时候,它可能会有两步来完成:

       1. 在 Items[Size] 的位置存放此元素;

       2. 增大 Size 的值。

       åœ¨å•çº¿ç¨‹è¿è¡Œçš„情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;

       è€Œå¦‚果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。

       é‚£å¥½ï¼ŒçŽ°åœ¨æˆ‘们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。

       ç¤ºä¾‹ç¨‹åºï¼š

       package test;

       import java.util.ArrayList;

       import java.util.List;

       public class ArrayListInThread implements Runnable {

       List<String> list1 = new ArrayList<String>();// not thread safe

       // List<String> list1 = Collections.synchronizedList(new ArrayList<String>());// thread safe

       public void run() {

       try {

       Thread.sleep((int)(Math.random() * 2));

       }

       catch (InterruptedException e) {

       e.printStackTrace();

       }

       list1.add(Thread.currentThread().getName());

       }

       public static void main(String[] args) throws InterruptedException {

       ThreadGroup group = new ThreadGroup("mygroup");

       ArrayListInThread t = new ArrayListInThread();

       for (int i = 0; i < ; i++) {

       Thread th = new Thread(group, t, String.valueOf(i));

       th.start();

       }

       while (group.activeCount() > 0) {

       Thread.sleep();

       }

       System.out.println();

       System.out.println(t.list1.size()); // it should be if thread safe collection is used.

       }

       }

线程安全的list之synchronizedList和CopyOnWriteArrayList

        在上篇文章中我们已经介绍了其他的一些list集合,如ArrayList、linkedlist等。不清楚的可以看下上篇文章 /p/ab5bf7

        但是向ArrayList这些会出现线程不安全的问题,我们该怎样解决呢?接下来就是要介绍我们线程安全的list集合synchronizedList和CopyOnWriteArrayList。

        synchronizedList的使用方式:

        从上面的使用方式中我们可以看出,synchronizedList是将List集合作为参数来创建的synchronizedList集合。

        synchronizedList为什么是线程安全的呢?

        我们先来看一下他的源码:

        我们大概贴了一些常用方法的源码,从上面的源码中我们可以看出,其实synchronizedList线程安全的原因是因为它几乎在每个方法中都使用了synchronized同步锁。

        synchronizedList官方文档中给出的使用方式是以下方式:

        在以上源码中我们可以看出,官方文档是建议我们在遍历的时候加锁处理的。但是既然内部方法以及加了锁,为什么在遍历的时候还需要加锁呢?我们来看一下它的遍历方法:

        从以上源码可以看出,虽然内部方法中大部分都已经加了锁,但是iterator方法却没有加锁处理。那么如果我们在遍历的时候不加锁会导致什么问题呢?

        试想我们在遍历的时候,不加锁的情况下,如果此时有其他线程对此集合进行add或者remove操作,那么这个时候就会导致数据丢失或者是脏数据的问题,所以如果我们对数据的要求较高,想要避免这方面问题的话,在遍历的时候也需要加锁进行处理。

        但是既然是使用synchronized加锁进行处理的,那肯定避免不了一些锁开销。有没有效率更好的方式呢?那就是我们另一个主要的并发集合CopyOnWriteArrayList。

        CopyOnWriteArrayList是在执行修改操作时,copy一份新的数组进行相关的操作,在执行完修改操作后将原来集合指向新的集合来完成修改操作。具体源码如下:

        从以上源码我们可以看出,它在执行add方法和remove方法的时候,分别创建了一个当前数组长度+1和-1的数组,将数据copy到新数组中,然后执行修改操作。修改完之后调用setArray方法来指向新的数组。在整个过程中是使用ReentrantLock可重入锁来保证不会有多个线程同时copy一个新的数组,从而造成的混乱。并且使用volatile修饰数组来保证修改后的可见性。读写操作互不影响,所以在整个过程中整个效率是非常高的。

        synchronizedList适合对数据要求较高的情况,但是因为读写全都加锁,所有效率较低。

        CopyOnWriteArrayList效率较高,适合读多写少的场景,因为在读的时候读的是旧集合,所以它的实时性不高。

java中Arraylist是干什么的?怎么用?

       java中的ArrayList就是传说中的动态数组,用MSDN中的源码说法,就是源码Array的复杂版本。

       它提供了如下一些好处:动态的源码增加和减少元素实现了ICollection和IList接口灵活的设置数组的大小 。

       ArrayList 的源码用法:

       ArrayList List = new ArrayList(); for( int

       i=0;i<;i++ ) //

       给数组增加个Int元素 List.Add(i); //..

       程序做一些处理

       List.RemoveAt(5);//

       将第6个元素移除 for( int i=0;i<3;i++ ) //

       再增加3个元素

       List.Add(i+); Int[] values =

       (Int[])List.ToArray(typeof(Int));//

       返回ArrayList包含的数组 。

扩展资料:

       Arraylist的源码examstack 源码定义:

       List 接口的大小可变数组的实现,位于API文档的源码java.util.ArrayList<E>。

       实现了所有可选列表操作,源码并允许包括 null 在内的源码所有元素。

       除了实现 List 接口外,源码此类还提供一些方法来操作内部用来存储列表的源码数组的大小。(此类大致上等同于 Vector 类,源码除了此类是源码不同步的。)

       size、源码小霸王php源码isEmpty、源码get、set、iterator 和 listIterator 操作都以固定时间运行。

       add 操作以分摊的固定时间 运行,也就是说,添加 n 个元素需要 O(n) 时间。

       其他所有操作都以线性时间运行(大体上讲)。

       与用于 LinkedList 实现的常数因子相比,此实现的常数因子较低。

       每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。

       它总是手机app源码查询至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。

       并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单

       在添加大量元素前,应用程序可以使用

        ensureCapacity 操作来增加 ArrayList

       实例的容量。这可以减少递增式再分配的数量。

       注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList

       实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。

       (结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的叮咚买菜插件源码大小;仅仅设置元素的值不是结构上的修改。)

       这一般通过对自然封装该列表的对象进行同步操作来完成。

       如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:

       List list = Collections.synchronizedList(new ArrayList(...));

       此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的。

       在创建迭代器之后,除非通过迭代器自身的

        remove 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出

       ConcurrentModificationException。

       因此,面对并发的修改,迭代器很快就会完全失败,而不是金芙蓉指标源码冒着在将来某个不确定时间发生任意不确定行为的风险。

       注意,迭代器的快速失败行为无法得到保证。

       因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出

       ConcurrentModificationException。

       因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测

        bug。

       

参考资料:

百度百科 ------ arraylist

fail-fast 和 fail-safe

        当一个或多个线程正在遍历一个集合(Collection),此时另一个线程修改了这个集合(添加,删除或修改)就称为并发修改。

        .

        官方文档在HashMap集合中对fail-fast的解释

        意思就是:这个迭代器(Iterator)被创建后,除了迭代器自身的方法(remove)可以改变集合的结构,其他情况改变了集合的结构,都将跑出一个 ConcurrentModificationException 异常。

        从上面的源码,可以发现迭代器在执行 next() 等方法的时候,都会调用一个方法 checkForComodification() ,而这个方法就是检查 modCount 是否等于 expectedModCount ,如果不等于就抛出 ConcurrentModificationException 异常。

        expectedModCount 这个变量的值在对象被创建的时候就赋予了一个固定的值 modCount ,这个值是不变的,当迭代器遍历元素的时候,如果 modCount 发生了改变,那就会抛出异常。

        查看源码可以发现,当对集合进行增删操作都会 modCount++ 。

        所以当我们对集合的元素的个数做出修改(添加、删除)的时候, modCount 的值就会发生改变,但对元素进行修改则不会改变 modCount 的值。

        保证在并发修改的时候,对所有会影响到 modCount 发生改变的地方,加上同步锁(synchronized),或者使用同步类容器 Collections.synchronizedList 。

        .

        fail-safe:任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出 ConcurrentModificationException 异常。

        两个问题:

        从源码可以看到,在对集合进行添加和删除元素的时候都进行加锁,然后让当前下标的元素添加或删除,最后将原数组的地址指向新的数组,完成复制。这里涉及到CopyOnWrite机制。

        这样做不会出现 fail-fast ,但是对集合进行增删操作都需要加锁,影响效率。同时增加对象容量可能会导致 OOM 。

        在遍历过程中,集合的元素并不一定是最终的元素集合,所以只能保证最终一致性。

关于 List 的线程不安全

       讨论 List 数据结构在多线程环境下的安全性问题。首先,答案是否定的,因为 List 类在 Java 中并未提供线程安全的实现,以牺牲一致性保证了效率。以 ArrayList 为例,其核心方法 add(E e) 未加锁,如源码所示:

       public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }

       为提升性能和并发性,未对并发操作进行同步处理,从而可能导致并发修改异常(ConcurrentModificationException)。通过在模拟代码中执行并发添加操作即可复现该异常。

       针对此问题,有以下几种解决策略:

       1. **使用 Vector**:Vector 类提供了线程安全的 add 方法,通过 synchronized 关键字对方法进行同步,确保并发安全。但其性能表现低于无锁的实现,特别是在高并发场景下。

       2. **利用 Collections.synchronizedList()**:此方法通过将非线程安全的 List 实例包装为同步的 List 实例,提供了一个简单的解决方式。通过以下代码即可实现:

       java

       List list = Collections.synchronizedList(new ArrayList());

       3. **采用 CopyOnWriteArrayList**:此类在添加元素时不会修改原有数据结构,而是在添加后创建新的数据结构副本。核心源码揭示了这一实现机制,具体为使用 ReentrantLock 上锁,复制现有数组,添加元素至新数组,并最终释放锁。

       综上所述,解决 List 类线程不安全问题的常见策略包括使用 Vector、Collections.synchronizedList() 方法或 CopyOnWriteArrayList 类。每种方法都有其适用场景和性能考量,开发者应根据具体需求选择合适的解决方案。

LinkedList(详细讲解)

       LinkedList是Java中实现List接口和Deque接口的双向链表,其独特结构使其支持高效的插入和删除操作,同时具备队列特性。非线程安全的LinkedList可通过Collections.synchronizedList方法进行同步处理。

       内部结构由Node节点构成,包含前驱节点、节点值和后继节点。类提供了多种操作方法,如空构造、基于集合创建链表、添加元素(add、addFirst、addLast)、根据索引获取或删除数据(get、remove、indexOf等)。在处理特定情况时,如获取头节点,getFirst()和element()会抛出异常,而getLast()和peekLast()则返回null。删除节点时,removeLast()和pollLast()行为不同,前者在链表为空时抛出异常,后者则返回null。

       总结,LinkedList提供了丰富的操作手段,适用于需要频繁插入和删除元素的场景,但需要注意其线程安全问题。通过学习其源码,我们可以深入了解其实现机制和使用方法。

Java的List如何实现线程安全?

       Java的List如何实现线程安全?

       Collections.synchronizedList(names);效率最高,线程安全

       Java的List是我们平时很常用的集合,线程安全对于高并发的场景也十分的重要,那么List如何才能实现线程安全呢 ?

       åŠ é”

       é¦–先大家会想到用Vector,这里我们就不讨论了,首先讨论的是加锁,例如下面的代码

       public class Synchronized{

       private List<String>  names = new LinkedList<>();

       public synchronized void addName(String name ){

       names.add("abc");

       }

       public String getName(Integer index){

       Lock lock =new ReentrantLock();

       lock.lock();

       try {

       return names.get(index);

       }catch (Exception e){

       e.printStackTrace();

       }

       finally {

       lock.unlock();

       }

       return null;

       }

       }

       synchronized一加,或者使用lock 可以实现线程安全,但是这样的List要是很多个,代码量会大大增加。

       java自带类

       åœ¨java中我找到自带有两种方法

       CopyOnWriteArrayList

       CopyOnWrite 写入时复制,它使一个List同步的替代品,通常情况下提供了更好的并发性,并且避免了再迭代时候对容器的加锁和复制。通常更适合用于迭代,在多插入的情况下由于多次的复制性能会一定的下降。

       ä¸‹é¢æ˜¯add方法的源代码

          public boolean add(E e) {

       final ReentrantLock lock = this.lock; // 加锁 只允许获得锁的线程访问

       lock.lock();

       try {

       Object[] elements = getArray();

       int len = elements.length;

       // 创建个长度加1的数组并复制过去

       Object[] newElements = Arrays.copyOf(elements, len + 1);

       newElements[len] = e; // 赋值

       setArray(newElements); // 设置内部的数组

       return true;

       } finally {

       lock.unlock();

       }

       }

       Collections.synchronizedList

       Collections中有许多这个系列的方法例如

       ä¸»è¦æ˜¯åˆ©ç”¨äº†è£…饰者模式对传入的集合进行调用 Collotions中有内部类SynchronizedList

         static class SynchronizedList<E>

       extends SynchronizedCollection<E>

       implements List<E> {

       private static final long serialVersionUID = -L;

       final List<E> list;

       SynchronizedList(List<E> list) {

       super(list);

       this.list = list;

       }

       public E get(int index) {

       synchronized (mutex) { return list.get(index);}

       }

       public E set(int index, E element) {

       synchronized (mutex) { return list.set(index, element);}

       }

       public void add(int index, E element) {

       synchronized (mutex) { list.add(index, element);}

       }

       public E remove(int index) {

       synchronized (mutex) { return list.remove(index);}

       }

       static class SynchronizedCollection<E> implements Collection<E>, Serializable {

       private static final long serialVersionUID = L;

       final Collection<E> c;  // Backing Collection

       final Object mutex;     // Object on which to synchronize

       è¿™é‡Œä¸Šé¢çš„mutex就是锁的对象 在构建时候可以指定锁的对象 主要使用synchronize关键字实现线程安全

          /

**

       * @serial include

       */

       static class SynchronizedList<E>

       extends SynchronizedCollection<E>

       implements List<E> {

       private static final long serialVersionUID = -L;

       final List<E> list;

       SynchronizedList(List<E> list) {

       super(list);

       this.list = list;

       }

       SynchronizedList(List<E> list, Object mutex) {

       super(list, mutex);

       this.list = list;

       }

       è¿™é‡Œåªæ˜¯åˆ—举SynchronizedList ,其他类类似,可以看下源码了解下。

       æµ‹è¯•

       public class Main {

       public static void main(String[] args) {

       List<String> names = new LinkedList<>();

       names.add("sub");

       names.add("jobs");

       // 同步方法1 内部使用lock

       long a = System.currentTimeMillis();

       List<String> strings = new CopyOnWriteArrayList<>(names);

       for (int i = 0; i < ; i++) {

       strings.add("param1");

       }

       long b = System.currentTimeMillis();

       // 同步方法2 装饰器模式使用 synchronized

       List<String> synchronizedList = Collections.synchronizedList(names);

       for (int i = 0; i < ; i++) {

       synchronizedList.add("param2");

       }

       long c = System.currentTimeMillis();

       System.out.println("CopyOnWriteArrayList time == "+(b-a));

       System.out.println("Collections.synchronizedList time == "+(c-b));

       }

       }

       ä¸¤è€…内部使用的方法都不一样,CopyOnWriteArrayList内部是使用lock进行加锁解锁完成单线程访问,synchronizedList使用的是synchronize

       è¿›è¡Œäº†æ¬¡æ·»åŠ åŽæ—¶é—´å¯¹æ¯”如下:

       å¯ä»¥çœ‹å‡ºæ¥è¿˜æ˜¯ä½¿ç”¨äº†synchronize的集合工具类在添加方面更加快一些,其他方法这里篇幅关系就不测试了,大家有兴趣去试一下。

相关推荐
一周热点