栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java 并发总结 线程安全的机制

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java 并发总结 线程安全的机制

这里写目录标题
    • 20.1 线程安全的机制
      • (1)synchronized
      • (2)显式锁
      • (3)volatile
      • (4)原子变量和CAS
      • (5)写时复制
      • (6)ThreadLocal
    • 参考目录

20.1 线程安全的机制

      线程表示一条单独的执行流,每个线程有自己的执行计数器,有自己的栈,但可以共享内存,共享内存是实现线程协作的基础,但共享内存有两个问题,竞态条件和内存可见性,之前章节探讨了解决这些问题的多种思路:

      ·使用synchronized;

      ·使用显式锁;

      ·使用volatile;

      ·使用原子变量和CAS;

      ·写时复制;

      ·使用ThreadLocal。

(1)synchronized

      synchronized简单易用,它只是一个关键字,大部分情况下,放到类的方法声明上就可以了,既可以解决竞态条件问题,也可以解决内存可见性问题。

      需要理解的是,它保护的是对象,而不是代码,只有对同一个对象 的synchronized方法调用,synchronized才能保证它们被顺序调用。对于实例方法,这个对象是this;对于静态方法,这个对象是类对象;对于 代码块,需要指定哪个对象。

      另外,需要注意,它不能尝试获取锁,也不响应中断,还可能会死锁。不过,相比显式锁,synchronized简单易用,JVM也可以不断优化它的实现,应该被优先使用。

(2)显式锁

      显式锁是相对于synchronized隐式锁而言的,它可以实现synchronized同样的功能,但需要程序员自己创建锁,调用锁相关的接 口,主要接口是Lock,主要实现类是Reen-trantLock。

      相比synchronized,显式锁支持以非阻塞方式获取锁,可以响应中断,可以限时,可以指定公平性,可以解决死锁问题,这使得它灵活得 多。

      在读多写少、读操作可以完全并行的场景中,可以使用读写锁以提高并发度,读写锁的接口是ReadWriteLock,实现类是 ReentrantReadWriteLock。

(3)volatile

      synchronized和显式锁都是锁,使用锁可以实现安全,但使用锁是有成本的,获取不到锁的线程还需要等待,会有线程的上下文切换开销 等。保证安全不一定需要锁。如果共享的对象只有一个,操作也只是进 行最简单的get/set操作,set也不依赖于之前的值,那就不存在竞态条件问题,而只有内存可见性问题,这时,在变量的声明上加上volatile就可以了。

(4)原子变量和CAS

      使用volatile,set的新值不能依赖于旧值,但很多时候,set的新值与原来的值有关,这时,也不一定需要锁,如果需要同步的代码比较简单,可以考虑原子变量,它们包含了一些以原子方式实现组合操作的方 法,对于并发环境中的计数、产生序列号等需求,考虑使用原子变量而非锁。

      原子变量的基础是CAS,一般的计算机系统都在硬件层次上直接支持CAS指令。通过循环CAS的方式实现原子更新是一种重要的思维。相 比synchronized,它是乐观的,而synchronized是悲观的;它是非阻塞式的,而synchronized是阻塞式的。CAS是Java并发包的基础,基于它可以 实现高效的、乐观、非阻塞式数据结构和算法,它也是并发包中锁、同步工具和各种容器的基础。

(5)写时复制

      之所以会有线程安全的问题,是因为多个线程并发读写同一个对象,如果每个线程读写的对象都是不同的,或者,如果共享访问的对象是只读的,不能修改,那也就不存在线程安全问题了。

      我们在介绍容器类CopyOnWriteArrayList和CopyOnWriteArraySet时介绍了写时复制技术,写时复制就是将共享访问的对象变为只读的,写的时候,再使用锁,保证只有一个线程写,写的线程不是直接修改原对象,而是新创建一个对象,对该对象修改完毕后,再原子性地修改共享访问的变量,让它指向新的对象。

(6)ThreadLocal

      ThreadLocal就是让每个线程,对同一个变量,都有自己的独有副本,每个线程实际访问的对象都是自己的,自然也就不存在线程安全问题了。

参考目录

绝大多数内容来自于:Java编程的逻辑 作者: 马俊昌(第20章 并发总结 20.1 线程安全的机制)

Java官方文档
https://docs.oracle.com/javase/specs/index.html

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/445356.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号