如何显示arraylist java的不同步性?

如何显示arraylist java的不同步性?,java,arraylist,vector,thread-safety,Java,Arraylist,Vector,Thread Safety,我们知道,ArrayList不是线程安全的,而VectorList是线程安全的。我想制作一个程序来显示操作是在VectorList而不是ArrayList中同步执行的。我面临的唯一问题是如何解决?什么样的手术 例如:-如果我们向列表中的任何一个添加值,程序只需添加值。 我试图制作一个,但意识到我的程序的同步性依赖于变量j,而不是ArrayList或VectorList public class ArrayDemo implements Runnable { private static

我们知道,ArrayList不是线程安全的,而VectorList是线程安全的。我想制作一个程序来显示操作是在VectorList而不是ArrayList中同步执行的。我面临的唯一问题是如何解决?什么样的手术
例如:-如果我们向列表中的任何一个添加值,程序只需添加值。
我试图制作一个,但意识到我的程序的同步性依赖于变量j,而不是ArrayList或VectorList

public class ArrayDemo implements Runnable {

    private static ArrayList<Integer> al = new ArrayList<Integer>();
    Random random = new Random();
    int j = 0;
    public void run() {
        while ( j < 10) {
            int i = random.nextInt(10);
            al.add(i);
            System.out.println(i + " "+ Thread.currentThread().getName());
            j++;
            //System.out.println(al.remove(0));
        }

    }

    public static void main(String[] args) {
        ArrayDemo ad = new ArrayDemo();
        Thread t = new Thread(ad);
        Thread t1 = new Thread(ad);
        t.start();t1.start();
    }

}
public类ArrayDemo实现可运行{
私有静态ArrayList al=新ArrayList();
随机=新随机();
int j=0;
公开募捐{
而(j<10){
int i=随机。nextInt(10);
al.添加(i);
System.out.println(i+“”+Thread.currentThread().getName());
j++;
//系统输出println(al.remove(0));
}
}
公共静态void main(字符串[]args){
ArrayDemo ad=新的ArrayDemo();
螺纹t=新螺纹(ad);
螺纹t1=新螺纹(ad);
t、 start();t1.start();
}
}
小型测试程序:

public class Test extends Thread {
    public static void main(String[] args) throws Exception {
        test(new Vector<>());
        test(new ArrayList<>());
        test(Collections.synchronizedList(new ArrayList<>()));
        test(new CopyOnWriteArrayList<>());
    }
    private static void test(final List<Integer> list) throws Exception {
        System.gc();
        long start = System.currentTimeMillis();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++)
            threads[i] = new Test(list);
        for (Thread thread : threads)
            thread.start();
        for (Thread thread : threads)
            thread.join();
        long end = System.currentTimeMillis();
        System.out.println(list.size() + " in " + (end - start) + "ms using " + list.getClass().getSimpleName());
    }
    private final List<Integer> list;
    Test(List<Integer> list) {
        this.list = list;
    }
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10000; i++)
                this.list.add(i);
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }
}
如您所见,with正常完成并返回
100000
,这是在10个并行线程中添加10000个值后的预期大小

您将看到两种不同的失败:

  • 在对
    add()
    的调用中,有三个线程与一起死亡
  • 即使三个失败的线程在添加任何内容之前立即死亡,其他7个线程仍应每个线程添加10000个值,总计
    70000
    个值,但列表仅包含
    32507
    个值,因此许多添加的值丢失
第三个测试是使用,其工作原理类似于
Vector

第四个测试,使用并发,也会生成正确的结果,但由于过度复制,速度要慢得多。但是,如果列表较小且很少更改,但经常读取,则它将比同步访问更快。
如果需要迭代列表,这尤其好,因为如果在迭代时修改列表,即使是
Vector
synchronizedList()
也会失败,而
CopyOnWriteArrayList
将迭代列表的快照


出于好奇,我也检查了一些实现:

test(new ArrayDeque<>());
test(new ConcurrentLinkedDeque<>());
test(new LinkedBlockingDeque<>());
如您所见,unsynchronized显示了“丢失值”症状,尽管它不会因异常而失败

Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: ...
两个并行实现,以及,运行良好且快速。

小型测试程序:

public class Test extends Thread {
    public static void main(String[] args) throws Exception {
        test(new Vector<>());
        test(new ArrayList<>());
        test(Collections.synchronizedList(new ArrayList<>()));
        test(new CopyOnWriteArrayList<>());
    }
    private static void test(final List<Integer> list) throws Exception {
        System.gc();
        long start = System.currentTimeMillis();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++)
            threads[i] = new Test(list);
        for (Thread thread : threads)
            thread.start();
        for (Thread thread : threads)
            thread.join();
        long end = System.currentTimeMillis();
        System.out.println(list.size() + " in " + (end - start) + "ms using " + list.getClass().getSimpleName());
    }
    private final List<Integer> list;
    Test(List<Integer> list) {
        this.list = list;
    }
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10000; i++)
                this.list.add(i);
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }
}
如您所见,with正常完成并返回
100000
,这是在10个并行线程中添加10000个值后的预期大小

您将看到两种不同的失败:

  • 在对
    add()
    的调用中,有三个线程与一起死亡
  • 即使三个失败的线程在添加任何内容之前立即死亡,其他7个线程仍应每个线程添加10000个值,总计
    70000
    个值,但列表仅包含
    32507
    个值,因此许多添加的值丢失
第三个测试是使用,其工作原理类似于
Vector

第四个测试,使用并发,也会生成正确的结果,但由于过度复制,速度要慢得多。但是,如果列表较小且很少更改,但经常读取,则它将比同步访问更快。
如果需要迭代列表,这尤其好,因为如果在迭代时修改列表,即使是
Vector
synchronizedList()
也会失败,而
CopyOnWriteArrayList
将迭代列表的快照


出于好奇,我也检查了一些实现:

test(new ArrayDeque<>());
test(new ConcurrentLinkedDeque<>());
test(new LinkedBlockingDeque<>());
如您所见,unsynchronized显示了“丢失值”症状,尽管它不会因异常而失败

Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: ...

这两个并发实现,和,工作得又好又快。

即使使用简单的程序,您也可以通过进行更多的循环迭代(10次可能不够)来证明
ArrayList
不是线程安全的,并减少减慢
ArrayList
上操作的其他代码,尤其是IO代码,如
System.out

我通过删除
Random
System.out
调用修改了您的原始代码。我只在循环的末尾添加了一个
System.out.println
,以显示可能的成功终止

但是,此代码并没有完全运行。相反,它抛出一个异常

Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: ...
从中学习的重要内容是,如果计时不正确,即使是类似的代码也可能不会遇到线程安全问题。这说明了为什么与线程相关的bug很难找到,并且在它们真正导致程序崩溃之前,它们可能潜伏在代码中很长时间

以下是修改后的代码:

import java.util.*;

public class ArrayDemo implements Runnable {

    private static ArrayList<Integer> al = new ArrayList<Integer>();

    int j = 0;
    public void run() {
        while (j < 10000) {
            al.add(new Integer(1));
            j++;
        }
        System.out.println("Array size: " + al.size());
    }

    public static void main(String[] args) {
        ArrayDemo ad = new ArrayDemo();
        Thread t = new Thread(ad);
        Thread t1 = new Thread(ad);
        t.start();
        t1.start();
    }

}
import java.util.*;
公共类ArrayDemo实现可运行{
私有静态ArrayList al=新ArrayList();
int j=0;
公开募捐{
而(j<10000){
加法(新整数(1));
j++;
}
System.out.println(“数组大小:+al.size());
}
公共静态void main(字符串[]args){
ArrayDemo ad=新的ArrayDemo();
螺纹t=新螺纹(ad);
螺纹t1=新螺纹(ad);
t、 start();
t1.start();
}
}

即使使用简单的程序,您也可以通过进行更多的循环迭代(10次可能不够)来证明
ArrayList
不是线程安全的,并减少其他减慢
ArrayList
上操作的代码,尤其是IO代码,如
System.out

我通过删除
Random
System.out
调用修改了您的原始代码。我只在循环的末尾添加了一个
System.out.println
,以显示可能的成功终止

但是,此代码并没有完全运行。相反,它抛出了一个例外