当前位置: 首页> 教育> 培训 > 柜子定制_中国科技成就作文素材排比句_简述网站推广的意义和方法_腾讯广告推广怎么做

柜子定制_中国科技成就作文素材排比句_简述网站推广的意义和方法_腾讯广告推广怎么做

时间:2025/7/12 14:26:34来源:https://blog.csdn.net/qq_73450329/article/details/144545982 浏览次数:0次
柜子定制_中国科技成就作文素材排比句_简述网站推广的意义和方法_腾讯广告推广怎么做

目录

List

1.vector

2.ArrayList

3.CopyonWriteArraylist

4.ArrayList变成线程安全的方式

Map

1.HashTable

2.HashMap

3.ConcurrentHashMap

Set

1.LinkedHashSe


List

1.vector

Vector 是线程安全的动态数组,其内部方法基本都经过synchronized修饰。Vector 在扩容时会提高 1 倍。(原来的2倍)

2.ArrayList

ArrayList 容量可变的非线程安全列表,其底层使用数组实现。 当几何扩容时,会创建更大的数组,并把原数组复制到新数组。ArrayList支持对元素的快速随机访问,但插入与删除速度很慢。ArrayList在添加元素时,如果当前元素个数已经达到了内部数组的容量上限,就会触发扩容操作。扩容时容量增加 50%。(原来的1.5倍)。之所以扩容是 1.5 倍,是因为 1.5 可以充分利用移位操作,减少浮点数或者运算时间和运算次数。

// 新容量计算 int newCapacity = oldCapacity + (oldCapacity >> 1);

ArrayList,add 增加元素的代码如下:

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

在高并发添加数据下,ArrayList会暴露三个问题;

  • 部分值为null(我们并没有add null进去)

部分值为null:当线程1走到了扩容那里发现当前size是9,而数组容量是10,所以不用扩容,这时候cpu让出执行权,线程2也进来了,发现size是9,而数组容量是10,所以不用扩容,这时候线程1继续执行,将数组下标索引为9的位置set值了,还没有来得及执行size++,这时候线程2也来执行了,又把数组下标索引为9的位置set了一遍,这时候两个先后进行size++,导致下标索引10的地方就为null了。

  • 索引越界异常

索引越界异常:线程1走到扩容那里发现当前size是9,数组容量是10不用扩容,cpu让出执行权,线程2也发现不用扩容,这时候数组的容量就是10,而线程1 set完之后size++,这时候线程2再进来size就是10,数组的大小只有10,而你要设置下标索引为10的就会越界(数组的下标索引从0开始);

  • size与我们add的数量不符

size与我们add的数量不符:这个基本上每次都会发生,因为size++本身就不是原子操作,可以分为三步:获取size的值,将size的值加1,将新的size值覆盖掉原来的,线程1和线程2拿到一样的size值加完了同时覆盖,就会导致一次没有加上,所以肯定不会与我们add的数量保持一致的。

3.CopyonWriteArraylist

CopyOnWriteArrayList底层也是通过一个数组保存数据,使用volatile关键字修饰数组,保证当前线程对数组对象重新赋值后,其他线程可以及时感知到。

private transient volatile Object[] array;

在写入操作时,加了一把互斥锁ReentrantLock以保证线程安全。

public boolean add(E e) {//获取锁final ReentrantLock lock = this.lock;//加锁lock.lock();try {//获取到当前List集合保存数据的数组Object[] elements = getArray();//获取该数组的长度(这是一个伏笔,同时len也是新数组的最后一个元素的索引值)int len = elements.length;//将当前数组拷贝一份的同时,让其长度加1Object[] newElements = Arrays.copyOf(elements, len + 1);//将加入的元素放在新数组最后一位,len不是旧数组长度吗,为什么现在用它当成新数组的最后一个元素的下标?建议自行画图推演,就很容易理解。newElements[len] = e;//替换引用,将数组的引用指向给新数组的地址setArray(newElements);return true;} finally {//释放锁lock.unlock();}
}

写入新元素时,首先会先将原来的数组拷贝一份并且让原来数组的长度+1后就得到了一个新数组,新数组里的元素和旧数组的元素一样并且长度比旧数组多一个长度,然后将新加入的元素放置都在新数组最后一个位置后,用新数组的地址替换掉老数组的地址就能得到最新的数据了。

在我们执行替换地址操作之前,读取的是老数组的数据,数据是有效数据;执行替换地址操作之后,读取的是新数组的数据,同样也是有效数据,而且使用该方式能比读写都加锁要更加的效率。

读操作,读是没有加锁的,所以读是一直都能读。

public E get(int index) {return get(getArray(), index);
}

4.ArrayList变成线程安全的方式

  • 使用Collections类的synchronizedList方法将ArrayList包装成线程安全的List:

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

  • 使用CopyOnWriteArrayList类代替ArrayList,它是一个线程安全的List实现:

CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>(arrayList);

  • 使用Vector类代替ArrayList,Vector是线程安全的List实现:

Vector<String> vector = new Vector<>(arrayList);

LinkedList 是 双向链表,非线程安全的。插入和删除速度更快,但随机访问速度更慢。

Map

1.HashTable

HashTable:数组+链表组成的,数组是 HashTable 的主体,链表则是主要为了解决哈希冲突而存在的。线程安全的哈希表,HashTable 的加锁方法是给每个方法加上 synchronized 关键字,这样锁住的是整个 Table 对象,不支持 null 键和值。默认初始容量为11,每次扩容变为原来的2n+1。

2.HashMap

HashMap: 基于哈希表的Map实现,存储键值对,通过键快速查找值。Key 无序,唯一;value 不要求有序,允许重复。底层数据结构为数组+链表,插入元素后如果链表长度大于阈值(默认为8),先判断数组长度是否小于64,如果小于,则扩充数组,反之将链表转化为红黑树,以减少搜索时间。

hashMap默认的负载因子是0.75,即如果hashmap中的元素个数超过了总容量75%,则会触发扩容,在扩充HashMap的时候,不需要重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”。默认初始容量为16,新容量是旧容量的2倍。

3.ConcurrentHashMap

ConcurrentHashMap:Node数组+链表+红黑树实现,线程安全的(jdk1.8以前Segment锁,1.8以后volatile + CAS 或者 synchronized)。

在 JDK 1.7 中它使用的是数组加链表的形式实现的,而数组又分为:大数组 Segment 和小数组 HashEntry。 Segment 是一种可重入锁(ReentrantLock)。分段锁技术将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。

JDK 1.8 ConcurrentHashMap 主要通过 volatile + CAS 或者 synchronized 来实现的线程安全的。添加元素时首先会判断容器是否为空:

  • 如果为空则使用 volatile 加 CAS 来初始化
  • 如果容器不为空,则根据存储的元素计算该位置是否为空。
    • 如果根据存储的元素计算结果为空,则利用 CAS 设置该节点;
    • 如果根据存储的元素计算结果不为空,则使用 synchronized ,然后,遍历桶中的数据,并替换或新增节点到桶中,最后再判断是否需要转为红黑树,这样就能保证并发访问时的线程安全了。

(ConcurrentHashMap通过对头结点加锁来保证线程安全的,锁的粒度相比 Segment 来说更小了,发生冲突和加锁的频率降低了,并发操作的性能就提高了。)

Set

set集合特点:Set集合中的元素是唯一的,不会出现重复的元素。

1.LinkedHashSet

记录插入顺序的集合通常指的是LinkedHashSet,它不仅保证元素的唯一性,还可以保持元素的插入顺序。当需要在Set集合中记录元素的插入顺序时,可以选择使用LinkedHashSet来实现。

详情见: Java集合面试题 | 小林coding

关键字:柜子定制_中国科技成就作文素材排比句_简述网站推广的意义和方法_腾讯广告推广怎么做

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: