Programmation Java multi-thread Liste simultanée de conteneurs La deuxième explication des classes simultanées

Programmation Java multi-thread Liste simultanée de conteneurs La deuxième explication des classes simultanées

contour

Dans cet article, nous allons vous expliquer en détail la liste des conteneurs en même temps et un code correspondant à ArrayList test, vecteur et de comparer les données à CopyOnWriteArrayList ajouté 1000 dans la liste des 100 sujets

Cet article est « Kaige partagée Java Concurrency expliquer le paquet UCC » série de tutoriels dans le sixième chapitre. Si vous voulez apprendre le système, les recommandations de Kaige (kaigejava) le premier à commencer à regarder.

Benpian dès le début, nous devons expliquer à expliquer les conteneurs simultanés de Java. Idée générale: introduire ce qui est des conteneurs concurrents. Expliquez ensuite la file d'attente et liées, relatives aux cartes liées à la liste. Cette série aura un bon nombre d'articles. Vous feriez mieux de suivre une école d'un.

Début de texte

Catégorie expliquer les conteneurs simultanés

CopyOneWriteArrayList

Copier-décalque: temps d'écriture qui est copié.

Nous savons que dans la liste de vactor d'origine est synchrone conteneur thread-safe. Ce CopyOneWriteArrayList peut être compris comme ses alternatives concurrentes.

Sa structure de données sous-jacente est numérique. Les différences entre ArrayList et mensonges: dans la liste pour ajouter ou éléments de suppression dans un objet quand la copie originale d'une collection, ajouter ou opération de suppression est une opération dans le nouvel objet. Une fois l'opération terminée, un nouveau tableau remplacera le tableau original.

look Let au code source de la méthode add CopyOnWriteArrayList:

0f9hvE1VwKe

éditer

Nous prenons une méthode setArray look:

0f9hvEkrUKu

éditer

Je trouve? Les variables utilisées sont transitoires et volatiles à deux modifications clés.

Dans l'article précédent, nous savons que la mémoire est volatile visibilité mot-clé. Alors mot-clé transitoire est de le faire? Nous regardons l'explication Wikipédia:

0f9hvFJf70S

éditer

Mots clés: variables membres transitoires mot-clé modifiés ne participent pas au processus de sérialisation.

Ajoutez le code source après les commentaires:

0f9hvG21I1Y

éditer

add public boolean (E e) {

finale de verrouillage ReentrantLock = this.lock;

// Obtenez le verrou

serrure.Serrure ();

try {

// Obtenez la collection originale

des éléments de l'objet [] = getArray ();

int len ​​= elements.length;

//将原集合copy一份到新的集合中。并设置新的集合的长度为原集合长度+1

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

//将需要新增的元数添加到新的素组中

newElements[len] = e;

//将新数组替换原来数据。 使用transient和volatitle关键字修饰的

setArray(newElements);

return true;

} finally {

lock.unlock();

}

}

代码很简单,大致流程如下:

先从ReentrantLock中获取到锁(这样在多线程下可以防止其他线程来修改容器list里面内容了);

通过arrays.copyOf方法copy出一份原有数组长度+1;

将要添加的元素赋值给copy出来的数组;

使用setArray方法将新得数组替换原有素组。

因为都是List集合。我们就拿ArrayList、vector以及CopyOnWriteArrayList进行比较:

ArrayList、vector以及CopyOnWriteArrayList比较

业务场景描述:

启动100个线程,向对象中添加1000个数据。查看各自运行结果耗时及插入数据总数。代码在文章最后凯哥会贴出来。

先用线程非安全的arrayList执行效果:

0f9hvGWrN0C

编辑

执行arryList时间为 : 112毫秒!

List.size() : 93266

我们发现list的长度不对。正确的应该是100*1000.从结果来看,arrayList丢数据了。

使用Vector执行后的效果:

0f9hvH2AVjk

编辑

执行vector时间为 : 98毫秒!

List.size() : 100000

执行的总数对,说下同步锁没有丢数据。

在来看看copyOnWriteArrayList执行效果:

0f9hvHWRrXc

编辑

执行copyOnWriteArrayList时间为 : 5951毫秒!

List.size() : 100000

运行后数据比较:

运行的类

运行时长

运行结果

ArrayList

112毫秒

93266

Vector

98毫秒

100000

copyOnWriteArrayList

5951毫秒

100000

从上面表格中我们可以看出非安全线程的容器会丢数据。使用copyOneWriteArrayList耗时很长。那是因为每次运行都要copyof一份。

总结

copyArrayList(这里凯哥就简写了):是读写分离的。在写的时候会复制一个新的数组来完成插入和修改或者删除操作之后,再将新的数组给array.读取的时候直接读取最新的数据。

因为在写的时候需要向主内存申请控件,导致写操作的时候,效率非常低的(虽然在操作时候比较慢得,但是在删除或者修改数组的头和尾的时候还是很快的。因为其数据结构决定查找头和尾快,而且执行不需要同步锁)

从上面表中,可以看出copyArrayList虽然保证了线程的安全性,但是写操作效率太low了。但是相比Vector来说,在并发安全方面的性能要比vector好;

CopyArrayList和Vector相比改进的地方:

Vector是在新增、删除、修改以及查询的时候都使用了Synchronized关键字来保证同步的。但是每个方法在执行的时候,都需要获取到锁,在获取锁等待的过程中性能就会大大的降低的。

CopyArrayList的改进:只是在新增和删除的方法上使用了ReentrantLock锁进行(这里凯哥就不截图源码了,自己可以看看源码)。在读的时候不加锁的。所以在读的方面性能要不vector性能要好。

所以,CopyArrayList支持读多写少的并发情况

CopyOnWriteArrayList的使用场景:

由于读操作不加锁,增删改三个操作加锁的,因此适用于读多写少的场景,

局限性:因为读的时候不加锁的,读的效率和普通的arrayList是一样的。但是请看读操作:

0f9hvI6KnNg

编辑

0f9hvIcZqMK

编辑

在get的时候array使用的是volatile修饰的。是内存可见的。所以可以说copyArrayList在读的时候不会出现arrayList读取到脏数据的问题。

Get(i)方法比较如下:

0f9hvJ9EyUC

编辑


附件:arrayList、vector、copyOnwriteArrayList比较的代码:

public static void main(String[] args) {
//使用线程不安全的arrayList
// List<String> arryList = new ArrayList<>();
//使用vector
// List<String> arryList = new Vector<>();


//使用copyOnWriteArrayList
List<String> arryList = new CopyOnWriteArrayList<>();

Random random = new Random();
Thread [] threadArr = new Thread[100];
CountDownLatch latch = new CountDownLatch(threadArr.length);
Long beginTime = System.currentTimeMillis();
for(int i = 0;i<threadArr.length;i++){
threadArr[i] = new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 1000; j++){
try {
arryList.add("value" + random.nextInt(100000));
catch (Exception e) {

}
}
latch.countDown();
}
});
}
for(Thread t : threadArr){
t.start();
}
try {
latch.await();
catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("执行copyOnWriteArrayList时间为 : " + (endTime-beginTime) + "毫秒!");
System.out.println("List.size() : " + arryList.size());
}


Je suppose que tu aimes

Origine blog.51cto.com/kaigejava/2479183
conseillé
Classement