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 异常
*
* 此异常是同时对集合进行修改时会抛出的异常
*/
}
}

最新文章

  1. gcc编译过程简述
  2. CPU的内部架构和工作原理 (转,相当不错)
  3. _IntfClear报错问题
  4. J2EE 第二阶段项目之编写代码(三)
  5. Stop PeopleCode Processing with Error
  6. 7-ZIP实现数据高度压缩
  7. lua进阶(二)
  8. 使用阿里云集成包快速搭建LAMP+FTP教程
  9. ViewPager和Fragment组合 v4包下的页面切换
  10. Json填充到Form中
  11. MATLAB模型预测控制(MPC,Model Predictive Control)
  12. ArcFace虹软与Dlib人脸识别对比
  13. nginx(三)反向代理和负载均衡
  14. Java+Selenium操作日期时间选择框插件
  15. .net 多线程之线程取消
  16. query did not return a unique result: 2错误的发生
  17. 如何将字符串格式的对象转换成真正的js对象?
  18. MongoDB添加仲裁节点报错replica set IDs do not match办法
  19. 05.Python网络爬虫之三种数据解析方式
  20. ES5的完美继承

热门文章

  1. 深入剖析.NETCORE中CORS(跨站资源共享)
  2. vb教程图文并茂
  3. mysql中的DDL,DML,DQL,DCL
  4. Java Web(5)-Servlet详解(上)
  5. MacOS下VUEJS简单入门
  6. Django学习路16_获取学生所在的班级名
  7. PHP XML DOM:DOM 是什么?
  8. PHP is_writeable() 函数
  9. luogu P1973 [NOI2011]NOI 嘉年华 dp
  10. docker 常用命令备忘录