Java集合类多线程下不安全解决方法

二、集合类不安全

2.1ArrayList线程不安全

2.1.1例子

单线程

public class NotSafeDemo {
    //单线程很安全
    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c");
        list.forEach(System.out::println);
    }
}

a
b
c

多线程

public class NotSafeDemo {
    //多线程不安全
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}
//报错java.util.ConcurrentModificationException并发修改异常

多线程下ArrayList报错是因为:ArrayList是线程不安全类,通过源码可以看出其add方法没有加锁,表示同一时刻可以有多个线程操作资源类,不安全

2.1.2解决ArrayList多线程不安全

方法一:使用Vector类(不推荐)

public class NotSafeDemo {
    //多线程不安全
    public static void main(String[] args) {
        List<String> list = new Vector<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}
image-20210925172621429

通过源码我们可以看到Vector类的add方法加了锁所以是安全的

方法二:Collections工具类

public class NotSafeDemo {
    //多线程不安全
    public static void main(String[] args) {
        List<String> list =  Collections.synchronizedList(new ArrayList<String>());
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

方法三:使用JUC中CopyOnWriteArrayList类(推荐)

CopyOnWriteArrayList在java.util.concurrent包中

public class NotSafeDemo {
    //多线程不安全
    public static void main(String[] args) {
          List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

除此之外,CopyOnWriteArrayList包中还有很多常用的线程安全的类

image-20210925194301298

为什么CopyOnWriteArrayList可以解决线程安全问题呢?

通过源码可以看出CopyOnWriteArrayList的add方法添加了可重入锁

image-20210925194617664

写入时复制(CopyOnWrite)思想:

  • 写入时复制(CopyOnWrite,简称COW)思想是计算机程序设计领域中的一种优化策略。其核心思想是,如果有多个调用者(Callers)同时要求相同的资源(如内存或者是磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者视图修改资源内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此做法主要的优点是如果调用者没有修改资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。读写分离,写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array

CopyOnWriteArrayList为什么并发安全且性能比Vector好:

  • Vector是增删改查方法都加了synchronized,保证同步,但是每个方法执行的时候都要去获得锁,性能就会大大下降,而CopyOnWriteArrayList 只是在增删改上加锁,但是读不加锁,在读方面的性能就好于Vector,CopyOnWriteArrayList支持读多写少的并发情况。

2.2HashSet线程不安全

2.2.1例子

public class NotSafeDemo {
    //多线程不安全
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

image-20210925201053383

2.2.2解决HashSet多线程不安全

方法一:Collections工具类

public class NotSafeDemo {
    //多线程不安全
    public static void main(String[] args) {
        Set<String> set = Collections.synchronizedSet(new HashSet<>());
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

方法二:使用CopyOnWriteArraySet类

public class NotSafeDemo {
    //多线程不安全
    public static void main(String[] args) {
        CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

2.3HashMap线程不安全

2.3.1例子

public class NotSafeDemo {
    //多线程不安全
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

image-20210925202611355

2.3.2解决HashMap多线程不安全

方法一:Collections工具类

public class NotSafeDemo {
    //多线程不安全
    public static void main(String[] args) {
        Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

方法二:使用ConcurrentHashMap类

public class NotSafeDemo {
    //多线程不安全
    public static void main(String[] args) {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值