「并发」线程不安全

70 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

1.5 集合中的线程不安全

1.5.1 什么叫做集合的线程不安全

问题根由:读和写操作同步执行

多个线程同时对同一个线程不安全的集合进行读和写操作导致出现ConcurrentModificationException异常

1.5.2 如何解决ArrayList的线程不安全问题

不直接使用ArrayList而是使用一些代替类

  • VectorVector替代ArrayList

    不使用ArrayList,而是使用Vector。其实现的add方法是加了synchronized关键字

    List<String> list = new Vector<>();
    
  • Collections工具类:使用Collections.synchronizedList(new ArrayList())

    List<String> list= Collections.synchronizedList(new ArrayList<>());
    
  • CopyOnWriteArrayList:使用JUC中的CopyOnWriteArrayList()

    List<String> list=new CopyOnWriteArrayList<>();
    

    写时复制技术:并发读,写入并不直接写入list,而是写入一个copyList然后将copyList覆盖原本的list

    进行add操作的时候

    1. 将原本的list复制出来一份copyList
    2. 在复制中的copyList进行写
    3. 写完之后与原本的那个list进行覆盖

1.5.2 HashSet的线程不安全问题

问题原因与ArrayList一致

CopyOnWriteArraySet:与CopyOnWriteArrayList解决方案一致

1.5.3 HashMap线程不安全

与ArrayList的原因是一致的

解决:

ConcurrentHashMap:在进行元素添加的时候增加了synchronized关键字

1.6 Callable接口

创建线程的一种方式

优点:可以在线程结束之后获取到返回值

Runnable接口和Callable接口区别

  1. 后者有返回值
  2. 如果出现异常会抛出

创建Callable的线程实现

使用FutureTask未来任务(就是可以支持另一个线程执行完成之后应该怎么做,比如下单,我需要扣完库存、扣完款才能给这个人发货。这个时候需要等待“扣库存”和“扣款”结束之后“发货”)

1.7 辅助类

countdownLatch计数器

直到所有线程运行一定次数才执行await里面的方法

  1. 使用
  2. 每个线程调用countDown使计数器-1
  3. 调用await方法,使值减到0的时候执行其中的代码

cyclicBarrier循环栅栏

让所有线程都等待,直到所有线程都达到一个固定的点才执行barrierAction方法

相当于多段操作,每段操作使用await()方法阻塞等待在那。比如部队里士兵训练。当所有士兵都跑完步才开始去往食堂,当所有士兵都打完饭才开始吃饭,当所有士兵都吃完饭才去洗澡

需要注意的是,协调好ThreadSize、PartiesSize、TaskSize之间的关系,不然就会导致永远等待

semaphore信号灯

多个资源被相互竞争,只有一个线程释放占用的资源,才能会有一个线程能占用这个资源

汽车抢占车位