技术标签: Java
列表实现有ArrayList、Vector、CopyOnWriteArrayList、Collections.synchronizedList(list)四种方式。
ArrayList是非线性安全,此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。即在一方在便利列表,而另一方在修改列表时,会报ConcurrentModificationException错误。而这不是唯一的并发时容易发生的错误,在多线程进行插入操作时,由于没有进行同步操作,容易丢失数据。
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;//使用了size++操作,会产生多线程数据丢失问题。
return true;
}
因此,在开发过程当中,ArrayList并不适用于多线程的操作。
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<T>(list) :
new SynchronizedList<T>(list));//根据不同的list类型最终实现不同的包装类。
}
其中,SynchronizedList对部分操作加上了synchronized关键字以保证线程安全。但其iterator()操作还不是线程安全的。部分SynchronizedList的代码如下:
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 ListIterator<E> listIterator() {
return list.listIterator(); // Must be manually synched by user 需要用户保证同步,否则仍然可能抛出ConcurrentModificationException
}
public ListIterator<E> listIterator(int index) {
return list.listIterator(index); // Must be manually synched by user <span style="font-family: Arial, Helvetica, sans-serif;">需要用户保证同步,否则仍然可能抛出ConcurrentModificationException</span>
}
/** The lock protecting all mutators */
transient final ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private volatile transient Object[] array;//保证了线程的可见性
public boolean add(E e) {
final ReentrantLock lock = this.lock;//ReentrantLock 保证了线程的可见性和顺序性,即保证了多线程安全。
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);//在原先数组基础之上新建长度+1的数组,并将原先数组当中的内容拷贝到新数组当中。
newElements[len] = e;//设值
setArray(newElements);//对新数组进行赋值
return true;
} finally {
lock.unlock();
}
}
其读操作代码如下:
public E get(int index) {
return (E)(getArray()[index]);
}
其没有加任何同步关键字,根据以上写操作的代码可知,其每次写操作都会进行一次数组复制操作,然后对新复制的数组进行些操作,不可能存在在同时又读写操作在同一个数组上(
不是同一个对象),而读操作并没有对数组修改,不会产生线程安全问题。Java中两个不同的引用指向同一个对象,当第一个引用指向另外一个对象时,第二个引用还将保持原来的对象。
package com.yang.test;
import org.junit.Test;
import java.util.*;
import java.util.concurrent.*;
/**
* Created with IntelliJ IDEA.
* User: yangzl2008
* Date: 14-9-18
* Time: 下午8:36
* To change this template use File | Settings | File Templates.
*/
public class Test02 {
private int NUM = 10000;
private int THREAD_COUNT = 16;
@Test
public void testAdd() throws Exception {
List<Integer> list1 = new CopyOnWriteArrayList<Integer>();
List<Integer> list2 = Collections.synchronizedList(new ArrayList<Integer>());
Vector<Integer> v = new Vector<Integer>();
CountDownLatch add_countDownLatch = new CountDownLatch(THREAD_COUNT);
ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
int add_copyCostTime = 0;
int add_synchCostTime = 0;
for (int i = 0; i < THREAD_COUNT; i++) {
add_copyCostTime += executor.submit(new AddTestTask(list1, add_countDownLatch)).get();
}
System.out.println("CopyOnWriteArrayList add method cost time is " + add_copyCostTime);
for (int i = 0; i < THREAD_COUNT; i++) {
add_synchCostTime += executor.submit(new AddTestTask(list2, add_countDownLatch)).get();
}
System.out.println("Collections.synchronizedList add method cost time is " + add_synchCostTime);
}
@Test
public void testGet() throws Exception {
List<Integer> list = initList();
List<Integer> list1 = new CopyOnWriteArrayList<Integer>(list);
List<Integer> list2 = Collections.synchronizedList(list);
int get_copyCostTime = 0;
int get_synchCostTime = 0;
ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
CountDownLatch get_countDownLatch = new CountDownLatch(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
get_copyCostTime += executor.submit(new GetTestTask(list1, get_countDownLatch)).get();
}
System.out.println("CopyOnWriteArrayList add method cost time is " + get_copyCostTime);
for (int i = 0; i < THREAD_COUNT; i++) {
get_synchCostTime += executor.submit(new GetTestTask(list2, get_countDownLatch)).get();
}
System.out.println("Collections.synchronizedList add method cost time is " + get_synchCostTime);
}
private List<Integer> initList() {
List<Integer> list = new ArrayList<Integer>();
int num = new Random().nextInt(1000);
for (int i = 0; i < NUM; i++) {
list.add(num);
}
return list;
}
class AddTestTask implements Callable<Integer> {
List<Integer> list;
CountDownLatch countDownLatch;
AddTestTask(List<Integer> list, CountDownLatch countDownLatch) {
this.list = list;
this.countDownLatch = countDownLatch;
}
@Override
public Integer call() throws Exception {
int num = new Random().nextInt(1000);
long start = System.currentTimeMillis();
for (int i = 0; i < NUM; i++) {
list.add(num);
}
long end = System.currentTimeMillis();
countDownLatch.countDown();
return (int) (end - start);
}
}
class GetTestTask implements Callable<Integer> {
List<Integer> list;
CountDownLatch countDownLatch;
GetTestTask(List<Integer> list, CountDownLatch countDownLatch) {
this.list = list;
this.countDownLatch = countDownLatch;
}
@Override
public Integer call() throws Exception {
int pos = new Random().nextInt(NUM);
long start = System.currentTimeMillis();
for (int i = 0; i < NUM; i++) {
list.get(pos);
}
long end = System.currentTimeMillis();
countDownLatch.countDown();
return (int) (end - start);
}
}
}
操作结果:
写操作 | 读操作 | |||
CopyOnWriteArrayList | Collections. synchronizedList |
CopyOnWriteArrayList | Collections. synchronizedList |
|
2 | 567 | 2 | 1 | 1 |
4 | 3088 | 3 | 2 | 2 |
8 | 25975 | 28 | 2 | 3 |
16 | 295936 | 44 | 2 | 6 |
32 | - | - | 3 | 8 |
64 | - | - | 7 | 21 |
128 | - | - | 9 | 38 |
文章浏览阅读756次。arXiv:2108.07797v1 [cs.CV] 17 Aug 2021思路根据样本和参考视频的相关性来提供重要的线索(因为同一个数据集的视频变化很小),以至于更准确的进行动作质量评估。使用一个Contrastive Regression (CoRe) 来成对的对比。尽管对比回归框架可以预测相对分数∆s,∆s通常取值范围很广(例如,对于潜水,∆s∈[−30,30])。因此,直接预测∆s仍然是一个很大的困难,因此提出一个group-aware regression tree将传统的得分回归转换_动作质量评估core
文章浏览阅读4.9k次,点赞5次,收藏39次。目录一、Kogge-Stone并行算法二、Kogge-Stone加法器三、Verilog设计Kogge-Stone加法器是利用Peter M. Kogge和Harold S.Stone于1972年提出的一种并行算法生成的一种树形加法器。一、Kogge-Stone并行算法Kogge和Stone根据一般m阶递归问题提出一种并行算法。本文介绍其一阶递归问题的并行结构,详细请阅读其..._kogge-stone adder
文章浏览阅读7.2k次。英语发音规则---字母组合oo的发音规律一、总结一句话总结:在英语单词中,字母组合oo多数读长音/u:/,少数读短音/ʊ/。另外,还有极少数的特殊情况读/ʌ/,在英语单词中,字母组合oo多数读长音/u:/,少数读短音/ʊ/。另外,还有极少数的特殊情况读/ʌ/,比如blood1、字母组合oo读/u:/的单词?food, mood 心情、语气roof , pro..._oo字母组合发音
文章浏览阅读8.4k次,点赞5次,收藏29次。点云凸包检测还是比较常用的,PCL自带的凸包检测 ConvexHull ,这个函数比较简单,设置凸包维度 setDimension即可,在这里记录一下头文件:#include <pcl/surface/convex_hull.h> pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pc...
文章浏览阅读218次。首先我们create-react-app创建一个项目antd 官网中推荐我们经过这样配置之后发现我们的react项目已经完成了对antd的按需加载但是如果我们想更改一下webpack配置的时候需要用到 npm run eject把一些配置暴露出来可能你执行npm run eject的时候会报错 没关系 我们执行一下git initgit add .git commit -m ‘init’链接一下git仓库接下来我们再启动项目你会发现项目启动报错了 提示我们找不到react
文章浏览阅读702次。利用c++语言以及graphics完成线段裁剪,裁剪区域为矩形框考虑三种情况:①线段在区域内;②线段在区域外;③线段一部分在区域外一部分在区域内源代码#include "stdafx.h"#include <graphics.h>#include <conio.h>//输入输出图形//定义宏变量#define LEFT 1#define RIGHT 2#define BOTTOM 4#define TOP 8int XL=200, XR=500, YB=500,_c++绘图程序 裁剪
文章浏览阅读772次。问题场景:定义一个类如下:@Componentpublic class MXTable{......}通过ApplicationContext.getBean("mXTable")获取这个Bean对象,但是为NULL,导致调用的时候出现空指针异常。问题原因:在使用注解生成Bean的时候,如果没有指定Bean的名称,如@Componet("mytable"),则Spring会使用默..._stereotype annotations suggest inconsistent component names:
文章浏览阅读10w+次,点赞25次,收藏60次。1.自己写入txt直接上核心代码:with open("douban.txt","w") as f: f.write("这是个测试!")12这句话自带文件关闭功能,所以和那些先open再write再close的方式来说,更加pythontic!结果就是这样:2.将文件输入(print)的内容写入txt我并不喜欢手写字符,更多时候用到的就是将程序跑出来的print写到txt中保存,比如_python txt写入
文章浏览阅读2.2k次。电感种类分类如下电感依铁芯形状不同有环型(toroidal)、E 型(E core)及工字鼓型(drum);依铁芯材质而言,主要有陶瓷芯(ceramic core)及两大软磁类,分别是铁氧体(ferrite)及粉末铁芯(metallic powder)等。依结构或封装方式不同有绕线式(wire wound)、多层式(multi-layer)及冲压式(molded),而绕线式又有非遮..._电感的种类
文章浏览阅读73次。原文地址:Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_2 by Keyvan Nayyeri 译注:本来想等别人翻译,见没人翻译,就利用中午休息的一点时间来翻译的,本来E文就不是很好,又匆忙,大家就将就着看吧。J .下午下班就可以回家过年拉。 感觉这系列文章前面部分的都很简单,建议大家直接去Scott Gu...
文章浏览阅读999次。使用VC调用matlab engine编程 首先要在电脑中装VC和MATLAB,我的电脑装的是VC6.0和MATLAB7.1. Matlab Engine 采用Client/Server的方式,通过ActivcX通道和Matlab接口来实现在VC编程环境中直接调用matlab中的指令。调用使用的函数是:engEvalSting。后面将讲到此函数的使用方法。_vcmatlab调用
文章浏览阅读83次。一.TypedArray类型TypedArray是漏洞中常见到的结构,手册用法有四1.new TypedArray(length);//byteLength=length * sizeof(TypeName);length当传入length参数时,一个内部数组缓冲区被创建,该缓存区的大小是传入的length乘以数组中每个元素的字节数,每个元素的值都为0.(译者注:每个元素的字节..._vc++中调用chakra