集合类不安全

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. pip install mysqlclient命令安装mysqlclient失败的解决办法

    错误情况: 解决方法: 到这个地址下载自己版本对应的资源 https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient 如下图: 我这里首先下载了  ...

  2. 策略模式+Spring配置类优化多if..else思路

    图示 1. 现状 场景: 假设设备上报不同类型的消息,我们要对不同类型的消息做不同的处理.如果我们通过if..else的方式处理的话会显得比较冗余. 例如: if("alarmEvent&q ...

  3. pyinstaller 安装报错,环境是python3.7

    在pycharm中安装,和直接输入pip install pyinstaller 均报错, 最后,输入pip install -i https://pypi.douban.com/simple/ py ...

  4. go run 和 go build的区别

    go run:编译并运行程序,但不会产生exe文件,运行速度也相应较慢 go build : 会产生exe文件,运行速度快

  5. 高效构建 vivo 企业级网络流量分析系统

    作者:vivo 互联网服务器团队- Ming Yujia 随着网络规模的快速发展,网络状况的良好与否已经直接关系到了企业的日常收益,故障中的每一秒都会导致大量的用户流失与经济亏损.因此,如何快速发现网 ...

  6. Unity三维数学总结

    三维向量和三角函数   三维向量 向量是指一个同时具有大小和方向,且满足平行四边形法则的几何对象. 向量的模 po点相对于世界坐标原点的距离: po.magnitude. 标准向量,归一向量,指的是将 ...

  7. nflsoj 5926 素数环

    题目非常简单,只需要判断相邻两个数的和是不是素数,素数的判断参考数论 不过要注意的一点是题目说的是一个环,所以首尾两个数的和也要是素数 我在输出的时候加上了 is_prime(path[n-1]+1) ...

  8. 8、Spring之基于注解的自动装配

    8.1.场景模拟 8.1.1.UserDao接口及实现类 package org.rain.spring.dao; /** * @author liaojy * @date 2023/8/5 - 18 ...

  9. Airtest遇到模拟器无法输入中文的情况该如何处理?

    此文章来源于项目官方公众号:"AirtestProject" 版权声明:允许转载,但转载必须保留原链接:请勿用作商业或者非法用途 1. 前言 最近有收到同学们的一些提问,使用Air ...

  10. 《SQL与数据库基础》14. 存储过程 · 存储函数

    目录 存储过程 基本语法 变量 系统变量 用户定义变量 局部变量 if判断 参数 case判断 while循环 repeat循环 loop循环 游标 条件处理程序 存储函数 本文以 MySQL 为例 ...