JUC并发编程学习(五)集合类不安全
集合类不安全
List不安全
单线程情况下集合类和很多其他的类都是安全的,因为同一时间只有一个线程在对他们进行修改,但是如果是多线程情况下,那么集合类就不一定是安全的,可能会出现一条线程正在修改的同时另一条线程启动来对这个集合进行修改,这种情况下就会导致发生并发修改异常(在jdk11的环境下多次测试该代码发现并无问题,但是学习教程中有该异常。原因:线程数量不够)

package org.example.unsafe;
import java.util.ArrayList;
import java.util.UUID;
public class Test1 {
public static void main(String[] args) {
ArrayList<String> sts = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
sts.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(sts);
},String.valueOf(i)).start();
}
}
}
重现该异常,通过for循环开更多线程
package org.example.unsafe;
import java.util.ArrayList;
import java.util.UUID;
public class Test1 {
public static void main(String[] args) {
MidiFireList midiFireList = new MidiFireList();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
midiFireList.midi();
}, "A").start();
}
for (int i = 0; i < 10; i++) {
new Thread(() -> {
midiFireList.midi();
}, "B").start();
}
for (int i = 0; i < 10; i++) {
new Thread(() -> {
midiFireList.midi();
}, "C").start();
}
}
}
class MidiFireList {
ArrayList<String> sts = new ArrayList<>();
public void midi() {
sts.add(Thread.currentThread() + ":" + UUID.randomUUID().toString().substring(0, 5));
System.out.println(sts);
}
}
成功重现异常

解决List的并发修改异常
1、通过使用List的子类Vector来操作,Vector默认时线程安全的,所以不会出现以上情况,Vector时jdk1.0时期就出现的,它的add方法使用了synchronized关键字来保证线程安全。

class MidiFireList {
//使用了线程安全的Vector集合类
List<String> sts = new Vector<>();
public void midi() {
sts.add(Thread.currentThread() + ":" + UUID.randomUUID().toString().substring(0, 5));
System.out.println(sts);
}
}
2、通过所有集合的父类Collections类的线程安全的方法创建一个ArraryList。

class MidiFireList {
List<String> sts = Collections.synchronizedList(new ArrayList<String>());
public void midi() {
sts.add(Thread.currentThread() + ":" + UUID.randomUUID().toString().substring(0, 5));
System.out.println(sts);
}
}
3、通过JUC包下的CopyOnWriteArrayList类来创建一个ArrayList,他内部的方法通过同步代码块和lock锁实现了线程安全的各种操作,缺点时少量线程操作时成本太高(CopyOnWrite写入时复制,COW思想,是计算机程序设计领域中的一种优化策略),在写入时复制一份,避免覆盖导致数据问题,读写分离思想


CopyOnWriteArrayList和Vector的在线程安全方面的区别,为什么要用CopyOnWriteArrayList
CopyOnWriteArrayList对比Vector,我们可以通过源码来看
CopyOnWriteArrayList:

Vector:

jdk1.8时的CopyOnWriteArrayList:

其实在jdk11之后的区别只在于同步代码块和同步方法的区别,可参考同步代码块和同步方法有什么区别 • Worktile社区和瞄一眼CopyOnWriteArrayList(jdk11) - 傅晓芸 - 博客园 (cnblogs.com)这两篇文章。
但是在jdk1.8时,CopyOnWriteArrayList的方法时单纯的通过Lock锁来实现同步的,没有使用synchronized关键字,因为会影响性能。
Set不安全
Set的不安全问题与List一样,解决方案如下
1、通过Collections的同步方法来创建一个线程安全的Set
class MidiFireList {
Set<String> set = Collections.synchronizedSet(new HashSet<>());
public void midi() {
set.add(Thread.currentThread() + ":" + UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}
}
2、通过CopyOnWriteArraySet类来创建线程安全的Set
class MidiFireList {
Set<String> set = new CopyOnWriteArraySet<>();
public void midi() {
set.add(Thread.currentThread() + ":" + UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}
}
HashSet的底层就是HashMap,他就不是一个新的东西
HashSet的add方法就时HashMap的put方法封装了一下

map的key是无法重复的,所以HashSet是无序的
Map不安全
Map解决方案
package org.example.unsafe;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest {
public static void main(String[] args) {
// HashMap是这样用的吗?不是工作中不用HashMap
// 默认等价于什么? new HashMap<>(16,0.75);
Map<String, String> map = new ConcurrentHashMap<>();
// 加载因子、初始化容量
for (int i = 0; i < 50; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
注意Map的并发类为ConcurrentHashMap
JUC并发编程学习(五)集合类不安全的更多相关文章
- JUC并发编程学习笔记
JUC并发编程学习笔记 狂神JUC并发编程 总的来说还可以,学到一些新知识,但很多是学过的了,深入的部分不多. 线程与进程 进程:一个程序,程序的集合,比如一个音乐播发器,QQ程序等.一个进程往往包含 ...
- [Java并发编程(五)] Java volatile 的实现原理
[Java并发编程(五)] Java volatile 的实现原理 简介 在多线程并发编程中 synchronized 和 volatile 都扮演着重要的角色,volatile 是轻量级的 sync ...
- Java并发编程学习笔记
Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...
- 并发编程学习笔记(8)----ThreadLocal的使用及源码分析
1. ThreadLocal的理解 ThreadLocal,顾名思义,就是线程的本地变量,ThreadLocal会为每个线程创建一个本地变量副本,使得使用ThreadLocal管理的变量在多线程的环境 ...
- 并发编程学习笔记(6)----公平锁和ReentrantReadWriteLock使用及原理
(一)公平锁 1.什么是公平锁? 公平锁指的是在某个线程释放锁之后,等待的线程获取锁的策略是以请求获取锁的时间为标准的,即使先请求获取锁的线程先拿到锁. 2.在java中的实现? 在java的并发包中 ...
- 并发编程学习笔记(4)----jdk5中提供的原子类及Lock使用及原理
(1)jdk中原子类的使用: jdk5中提供了很多原子类,它会使变量的操作变成原子性的. 原子性:原子性指的是一个操作是不可中断的,即使是在多个线程一起操作的情况下,一个操作一旦开始,就不会被其他线程 ...
- Java并发编程学习前期知识下篇
Java并发编程学习前期知识下篇 通过上一篇<Java并发编程学习前期知识上篇>我们知道了在Java并发中的可见性是什么?volatile的定义以及JMM的定义.我们先来看看几个大厂真实的 ...
- 并发编程(五)LockSupport
并发编程(五)LockSupport LockSupport 提供 park() 和 unpark() 方法实现阻塞线程和解除线程阻塞,实现的阻塞和解除阻塞是基于"许可(permit)&qu ...
- 并发编程学习笔记(15)----Executor框架的使用
Executor执行已提交的 Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方法.通常使用 Executor 而不是显式地创建 ...
- 并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理
1. 概述 1.1 什么是线程池 与jdbc连接池类似,在创建线程池或销毁线程时,会消耗大量的系统资源,因此在java中提出了线程池的概念,预先创建好固定数量的线程,当有任务需要线程去执行时,不用再去 ...
随机推荐
- Linux下手工编译libiconv库的小问题
我的电脑是 Ubuntu 14.04 LTS, 自己手工编译 php5.6, 打开 ZEND_EXTRA_LIBS='-liconv' 时, 发现没有安装 libiconv, 也就是编码转换的库, 所 ...
- Vue报错:Invalid handler for event "changeUI": got undefined
解决方案 错误代码如下所示: <router-view @hideBox="hideLoginRegisterBox" @changeUI="changeLogin ...
- Android 架构模式如何选择
作者:vivo 互联网客户端团队-Xu Jie Android架构模式飞速演进,目前已经有MVC.MVP.MVVM.MVI.到底哪一个才是自己业务场景最需要的,不深入理解的话是无法进行选择的.这篇文章 ...
- 通过 tree shaking 移除无用代码
tree shaking 依赖于ES Module 的静态语法分析,在项目编译时移除无用的代码以减少文件体积. usedExports 在文件中,我们可能定义了变量但是暂时又没有用到,这样会造成空间的 ...
- 大数据请把文章推给想了解DLL的人
DLL(Dynamic Link Library)动态链接库在 webpack 中用来将可共享且不常改变的代码抽取成公共的库. 没有使用 DLL react 和 react-dom 在 react 项 ...
- 树链剖分 | 洛谷 P4114 Qtree1
前言 题目链接:洛谷 P4114 Qtree1 前置知识:树链剖分 题意 给定一棵树,有修改边权和查询两点之间边权最大值两种操作,对于每个查询输出结果. 解析 已经在前置博客里提到,树链剖分 可以将树 ...
- [ABC284F] ABCBAC
2023-01-09 题目 题目传送门 翻译 翻译 难度&重要性(1~10):2.5 题目来源 AtCoder 题目算法 Z函数,KMP,字符串Hash 解题思路 对于一个 \(f_S\),我 ...
- Linux 主机磁盘繁忙度监控实战shell脚本
Linux 磁盘繁忙度是指磁盘的使用率和活动水平.可以通过一些工具来监测磁盘繁忙度,如 iostat.iotop.sar 等. 其中,iostat 是一个常用的工具,可以提供关于磁盘活动的详细统计信息 ...
- API接口获取快手商品详情(封装代码)
快手是中国最大的短视频平台之一,也是许多电商企业进行推广的重要渠道.为了更好地了解快手的商品信息,我们可以通过API接口来获取商品详情. 首先,我们需要了解快手API接口和相应的文档 接下来,我们需要 ...
- 微信小程序2--WXML与WXSS
编辑WXML文件 我们在开发者工具里打开之前修改的模板小程序home文件夹下的home.wxml,里面有如下代码 <!--pages/home/home.wxml--> <text& ...