1.1 集合安全问题

1.1.1 ListNotSafe

首先看一个例子:

public class ListNotSafeDemo {
//ArrayList线程不安全
public static void main(String[] args) throws Exception {
List<Integer> list = new ArrayList<>();
List<Object> synchronizedList = Collections.synchronizedList(new ArrayList<>());
CopyOnWriteArrayList<Object> copyOnWriteArrayList = new CopyOnWriteArrayList<>(); //30个线程对list进行修改和读取操作
for (int i = 0; i < 30; ++i)
new Thread(() -> {
list.add(5);
System.out.println(list);
}).start();
}
}

用ArrayList在多线程操作下的结果:

[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
Process finished with exit code 0

可以看到结果是多个线程互相争夺对list的写入权导致结果不是依次增长。

解决办法:

  • Vector (保证安全,效率低)
  • Collections.synchronizedList(new ArrayList<>()) (保证安全,效率低)
  • CopyOnWriteArrayList

采用线程安全之后的效果:

[5]
[5, 5]
[5, 5, 5]
[5, 5, 5, 5]
[5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5] Process finished with exit code 0

对于第三种写时复制说一下它的原理:

Copy-On-Write是一种程序设计的优化方法,多线程在不修改对象时可以共享一个对象地址空间,如果某一个线程要求修改对象时,需要首先将原来对象复制一份,在新复制的对象地址空间上修改对象内容,其他线程访问此对象时还是访问之前的旧对象,当新对象修改完成后,再将旧对象的指针指向新对象,这种优化方法适合读多写少的场景,体现了读写分离思想。从JDK1.5起;">使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。

1.1.2 MapNotSafe

关于Map都知道的是HashMap是线程不安全的(HashMap的初始化默认容量为16,扩容后为原来的2倍),在多个线程同时进行操作的时候大概率可能会抛出 ConcurrentModificationException 异常。此异常是同时对集合进行修改时会抛出的异常

突然想到一个知识点:loadFactory为什么是0.75?

答:哈希因子越小,空间利用率越低,那么数组存放的元素就越少,后果就是冲突减少了,查询效率提高了,但是空间大大浪费

如果哈希因子为1,数组倒是用满了,但是冲突的几率变大了,查询变慢,所以权衡以上设置为0.75。

。。。。。跑题了。。。。。

  • Map里也有线程安全的:HashTable,但是HashTable的同步基于synchronized(所谓的重锁),所以他的效率并不高。
  • Collections仍然提供了 SynchronizedMap,底层还是基于 synchronized,不建议使用这个
  • 重点来了ConcurrentHashMap: 并发map,很好的支持高性能和高并发.jdk1.7之前使用分段数组+链表实现,jdk1.8后使用 数组+链表/红黑树实现.jdk1.7之前给每段数据加锁,当一个线程访问其中一段数据时,其他数据也能被其他线程访问,也是非常的高效jdk1.8后使用数组+链表/红黑树实现,其扩容等机制与HashMap一样,但是控制并发的方法改为了CAS+synchronized。synchronized锁的只是链表的首节点或红黑树的首节点,这样一来,只要节点不冲突(hash不冲突),synchronized也不会触发,更加高效
  • ConcurrentSkipListMap : 跳表map

1.1.3 SetNotSafe

首先来说不安全的set:

  • HashSet:

    • HashSet底层使用HashMap实现,它使用一个固定的Object作为Value,用户写的值为Key
    • HashSet是线程不安全的,允许存null,不保证对象的有序性,因为HashSet的底层使用的是HashMap
    • 所以它默认的初始化容量为16

      线程安全的Set:
    • 1: 和List一样Collections仍然提供了synchronizedSet
    • 2: CopyOnWriteArraySet,值得一提的是:CopyOnWriteArraySet使用CopyOnWriteArrayList实现

      不安全的DEMO:
public static void main(String[] args) {
Set<String> set = new HashSet<>();
Runnable run = ()->{
set.add(UUID.randomUUID().toString());
System.out.println(set);
}; //30个线程对set进行修改和读取操作
for(int i = 0 ; i < 100; ++i)
{
new Thread(run).start();
} /**
*
* 大概率可能会抛出 ConcurrentModificationException 异常
*
* 此异常是同时对集合进行修改时会抛出的异常
*/
}
}

JUC---02的更多相关文章

  1. Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

    本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...

  2. java多线程系类:JUC线程池:02之线程池原理(一)

    在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...

  3. Java多线程系列--“JUC原子类”02之 AtomicLong原子类

    概要 AtomicInteger, AtomicLong和AtomicBoolean这3个基本类型的原子类的原理和用法相似.本章以AtomicLong对基本类型的原子类进行介绍.内容包括:Atomic ...

  4. Java多线程系列--“JUC集合”02之 CopyOnWriteArrayList

    概要 本章是"JUC系列"的CopyOnWriteArrayList篇.接下来,会先对CopyOnWriteArrayList进行基本介绍,然后再说明它的原理,接着通过代码去分析, ...

  5. Java多线程系列--“JUC线程池”02之 线程池原理(一)

    概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...

  6. java多线程系类:JUC原子类:02之AtomicLog原子类

    概要 AtomicInteger, AtomicLong和AtomicBoolean这3个基本类型的原子类的原理和用法相似.本章以AtomicLong对基本类型的原子类进行介绍.内容包括:Atomic ...

  7. Java多线程系列 JUC线程池02 线程池原理解析(一)

    转载  http://www.cnblogs.com/skywang12345/p/3509960.html ; http://www.cnblogs.com/skywang12345/p/35099 ...

  8. JUC 并发编程--02,生产者和消费者 synchronized的写法 , juc的写法. Condition的用法

    synchronized的写法 class PCdemo{ public static void main(String[] args) { //多个线程操作同一资源 Data data = new ...

  9. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...

  10. Java多线程系列--“JUC锁”04之 公平锁(二)

    概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...

随机推荐

  1. APP自动化 -- 滑动解锁、滑动验证

    一.解锁 1.代码 2.效果 1)执行效果 2)点位效果

  2. ELasticSearch(五)ES集群原理与搭建

    一.ES集群原理 查看集群健康状况:URL+ /GET _cat/health (1).ES基本概念名词 Cluster 代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产 ...

  3. C#winform将dll封装到exe当中

    我们在在winform程序时经常会用到外部dll,正常情况下,我的exe运行文件旁就需要这些dll文件相伴,总感觉不爽~~特别是要把软件给别人的时候,如果DLL比较多或者没有放在同一个地方,那麻烦大了 ...

  4. Day05_vue入门

    学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"乐优商城"获取视频和教程资料! b站在线视频 学习目标 ...

  5. python基础day6_字典dict

    数据类型划分:可变数据类型.不可变数据类型 不可变数据类型(又叫可哈希):元祖,bool ,int,str, 可变数据类型(又叫不可哈希):list,dict,set(集合) dict的key必须是不 ...

  6. HTML 基础- 4个实例

    HTML 基础- 4个实例 不要担心本章中您还没有学过的例子,高佣联盟 www.cgewang.com 您将在下面的章节中学到它们. HTML 标题 HTML 标题(Heading)是通过<h1 ...

  7. PHP var_export() 函数

    var_export() 函数用于输出或返回一个变量,以字符串形式表示.高佣联盟 www.cgewang.com高佣联盟 www.cgewang.com var_export() 函数返回关于传递给该 ...

  8. JAVA设计模式 5【结构型】代理模式的理解与使用

    今天要开始我们结构型 设计模式的学习,设计模式源于生活,还是希望能通过生活中的一些小栗子去理解学习它,而不是为了学习而学习这些东西. 结构型设计模式 结构型设计模式又分为 类 结构型 对象 结构型 前 ...

  9. HashMap源码(数组算法)

    Jdk1.8初始化hashMap容量的算法 static final int tableSizeFor(int cap) { // 先减1,避免传进来的本来就是2的n次幂,导致算出来多了一次幂,比如传 ...

  10. 【SDOI2012】Longge 的问题 题解(欧拉函数)

    前言:还算比较简单的数学题,我这种数学蒟蒻也会做QAQ. --------------- 题意:求$\sum\limits_{i=1}^n gcd(i,n)$的值. 设$gcd(i,n)=d$,即$d ...