集合类不安全

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并发编程学习(五)集合类不安全的更多相关文章

  1. JUC并发编程学习笔记

    JUC并发编程学习笔记 狂神JUC并发编程 总的来说还可以,学到一些新知识,但很多是学过的了,深入的部分不多. 线程与进程 进程:一个程序,程序的集合,比如一个音乐播发器,QQ程序等.一个进程往往包含 ...

  2. [Java并发编程(五)] Java volatile 的实现原理

    [Java并发编程(五)] Java volatile 的实现原理 简介 在多线程并发编程中 synchronized 和 volatile 都扮演着重要的角色,volatile 是轻量级的 sync ...

  3. Java并发编程学习笔记

    Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...

  4. 并发编程学习笔记(8)----ThreadLocal的使用及源码分析

    1. ThreadLocal的理解 ThreadLocal,顾名思义,就是线程的本地变量,ThreadLocal会为每个线程创建一个本地变量副本,使得使用ThreadLocal管理的变量在多线程的环境 ...

  5. 并发编程学习笔记(6)----公平锁和ReentrantReadWriteLock使用及原理

    (一)公平锁 1.什么是公平锁? 公平锁指的是在某个线程释放锁之后,等待的线程获取锁的策略是以请求获取锁的时间为标准的,即使先请求获取锁的线程先拿到锁. 2.在java中的实现? 在java的并发包中 ...

  6. 并发编程学习笔记(4)----jdk5中提供的原子类及Lock使用及原理

    (1)jdk中原子类的使用: jdk5中提供了很多原子类,它会使变量的操作变成原子性的. 原子性:原子性指的是一个操作是不可中断的,即使是在多个线程一起操作的情况下,一个操作一旦开始,就不会被其他线程 ...

  7. Java并发编程学习前期知识下篇

    Java并发编程学习前期知识下篇 通过上一篇<Java并发编程学习前期知识上篇>我们知道了在Java并发中的可见性是什么?volatile的定义以及JMM的定义.我们先来看看几个大厂真实的 ...

  8. 并发编程(五)LockSupport

    并发编程(五)LockSupport LockSupport 提供 park() 和 unpark() 方法实现阻塞线程和解除线程阻塞,实现的阻塞和解除阻塞是基于"许可(permit)&qu ...

  9. 并发编程学习笔记(15)----Executor框架的使用

    Executor执行已提交的 Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方法.通常使用 Executor 而不是显式地创建 ...

  10. 并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理

    1. 概述 1.1 什么是线程池 与jdbc连接池类似,在创建线程池或销毁线程时,会消耗大量的系统资源,因此在java中提出了线程池的概念,预先创建好固定数量的线程,当有任务需要线程去执行时,不用再去 ...

随机推荐

  1. MAUI Blazor 显示本地图片的新思路

    前言 好久没写文章了,水一篇 关于MAUI Blazor 显示本地图片这个问题,有大佬发过了. 就是 token 大佬的那篇 Blazor Hybrid (Blazor混合开发)更好的读取本地图片 主 ...

  2. 利用Python爬取免费代理IP

    # 2019/9/8 # 思路: 1.找到一个免费的ip代理网站(如:西刺代理) # # 2.爬取ip(常规爬取requests+BeautifulSoup) # # 3.验证ip有效性(携带爬取到的 ...

  3. 【pandas小技巧】--按类型选择列

    本篇介绍的是pandas选择列数据的一个小技巧.之前已经介绍了很多选择列数据的方式,比如loc,iloc函数,按列名称选择,按条件选择等等. 这次介绍的是按照列的数据类型来选择列,按类型选择列可以帮助 ...

  4. win10安装mysql-8.0.19-winx64

    第一步:去官网下载安装 (重点)第二步:先解压,然后在mysql下创建一个my.ini文件,更改my.ini文件里面的两行安装目录,第二行加上\data,my.ini文件不能多或少一个符号,内容见文章 ...

  5. 【Unity3D】反射和折射

    1 前言 ​ 立方体纹理(Cubemap)和天空盒子(Skybox)中介绍了生成立方体纹理和制作天空盒子的方法,本文将使用立方体纹理进行采样,实现反射.菲涅耳反射和折射效果.另外,本文还使用了 Gra ...

  6. 日志监控平台搭建(Loki+promtail+grafana)

    搭建Loki+promtail+grafana日志监控平台,可以直接在grafana的UI界面查看系统应用日志,使日志查看起来更方便.快捷.   Loki:主服务器,负责存储日志和处理查询. Prom ...

  7. 定义一个类,在实例化的时候,抛出NameError异常

    代码1:class cla: def __init__(self): #raise NameError # 抛出异常 print(r) cla() 运行截图:

  8. 解密Prompt系列13. LLM Agent-指令微调方案: Toolformer & Gorilla

    上一章我们介绍了基于Prompt范式的工具调用方案,这一章介绍基于模型微调,支持任意多工具组合调用,复杂调用的方案.多工具调用核心需要解决3个问题,在哪个位置进行工具调用(where), 从众多工具中 ...

  9. java与es8实战之一:以builder pattern开篇

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于<java与es8实战>系列 < ...

  10. 文心一言 VS 讯飞星火 VS chatgpt (82)-- 算法导论8.1 1题

    一.如果用go语言,在一棵比较排序算法的决策树中,一个叶结点可能的最小深度是多少? 文心一言: 在一个排序决策树(如二叉搜索树)中,每个叶节点的最小深度等于输入数据中最大元素与最小元素之间的位距离.这 ...