11.并行流-多线程的安全问题-加同步锁/使用线程安全的容器/toArray方法或者collect

42 阅读1分钟

11.并行流-多线程的安全问题-加同步锁/使用线程安全的容器/toArray方法或者collect

针对线程安全问题,我们可以采取的解决方案有哪些呢?

  • 加同步锁
  • 使用线程安全的容器
  • toArray方法或者collect

加同步锁

在多线程的处理下,肯定会出现数据安全问题,如下:

多线程安全问题复现

@Test
@DisplayName("并行流-多线程的数据安全问题")
public void test31() {
    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < 1000 ; i++) {
        list.add(i);
    }
    System.out.println(list.size());

    List<Integer> newList = new ArrayList<>();
    list.parallelStream().forEach(newList::add);

    System.out.println(newList.size());

}

Result:

1000
964





java.lang.Exception: No tests found matching Method test31(com.lzh.ShizhanStreamTests) from org.junit.vintage.engine.descriptor.RunnerRequest@11758f2a

	at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:40)

针对这个问题,我们的解决方法如下:【加同步锁】

@org.junit.Test
@DisplayName("解决并行流-多线程的数据安全问题")
public void test32() {
    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < 1000 ; i++) {
        list.add(i);
    }
    System.out.println(list.size());

    List<Integer> newList = new ArrayList<>();

    Object obj = new Object();

    list.parallelStream().forEach(s-> {
        synchronized(obj){
            newList.add(s);
        }
       });

    System.out.println(newList.size());

}
1000
1000

线程安全问题:

@org.junit.Test
@DisplayName("并行流2-多线程的数据安全问题")
public void test33() {
   List<Integer> newList = new ArrayList<Integer>();
    IntStream.rangeClosed(0,1000).parallel().forEach(i -> newList.add(i));

}
消耗时间4


java.lang.ArrayIndexOutOfBoundsException: 15

【加同步锁】

@org.junit.Test
@DisplayName("解决并行流2-多线程的数据安全问题")
public void test33() {
    Object obj = new Object();
    List<Integer> newList = new ArrayList<Integer>();
    IntStream.rangeClosed(0,1000).parallel().forEach(i -> {
        synchronized (obj){
            newList.add(i);
        }
    });

    System.out.println(newList.size());

}
1001

使用线程安全的容器

Vector 是一个线程安全的容器

@org.junit.Test
@DisplayName("使用线程安全的容器")
public void test34() {
    Vector v = new Vector();
    Object obj = new Object();
    IntStream.rangeClosed(0,1000).parallel().forEach(i -> {
        synchronized (obj){
            v.add(i);
        }
    });

    System.out.println(v.size());

}

不过网上说,不建议使用Vector

image.png

其他解决线程安全问题的解决方案

我们还可以通过Stream中的toArray方法或者collect方法,这两个方法就是满足线程安全的。

@org.junit.Test
@DisplayName("toArray方法解决线程安全")
public void test35() {
   List<Integer> newList = new ArrayList<Integer>();
    List<Integer> collect = IntStream.rangeClosed(0, 1000).parallel()
            .boxed()
            .collect(Collectors.toList());
    System.out.println("collect = " + collect);
    System.out.println(collect.size());

}
collect = [0, 1, 2, 3,...1001]
1001