《Java并发编程实战》课程笔记(二)

这篇具有很好参考价值的文章主要介绍了《Java并发编程实战》课程笔记(二)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

可见性、原子性和有序性问题:并发编程 Bug 的源头

源头之一:缓存导致的可见性问题

  • 在单核时代,所有的线程都是在一颗 CPU 上执行,CPU 缓存与内存的数据一致性容易解决。
    《Java并发编程实战》课程笔记(二)
    • 因为所有线程都是操作同一个 CPU 的缓存,一个线程对缓存的写,对另外一个线程来说一定是可见的。
    • 一个线程对共享变量的修改,另外一个线程能够立刻看到,我们称为可见性。
  • 多核时代,每颗 CPU 都有自己的缓存,这时 CPU 缓存与内存的数据一致性就没那么容易解决了,当多个线程在不同的 CPU 上执行时,这些线程操作的是不同的 CPU 缓存。
    《Java并发编程实战》课程笔记(二)

源头之二:线程切换带来的原子性问题

  • 操作系统允许某个进程执行一小段时间,例如 50 毫秒,过了 50 毫秒操作系统就会重新选择一个进程来执行(我们称为“任务切换”),这个 50 毫秒称为“时间片”。
  • 操作系统做任务切换,可以发生在任何一条 CPU 指令执行完,是的,是 CPU 指令,而不是高级语言里的一条语句。
    《Java并发编程实战》课程笔记(二)
    • 我们假设 count=0,如果线程 A 在指令 1 执行完后做线程切换,线程 A 和线程 B 按照下图的序列执行,那么我们会发现两个线程都执行了 count +=1 的操作,但是得到的结果不是我们期望的 2,而是 1。
    • 我们潜意识里面觉得 count += 1 这个操作是一个不可分割的整体,就像一个原子一样,线程的切换可以发生在 count+=1 之前,也可以发生在 count += 1 之后,但就是不会发生在中间。
    • 我们把一个或者多个操作在 CPU 执行的过程中不被中断的特性称为原子性。
    • CPU 能保证的原子操作是 CPU 指令级别的,而不是高级语言的操作符,这是违背我们直觉的地方。
    • 因此,很多时候我们需要在高级语言层面保证操作的原子性。

源头之三:编译优化带来的有序性问题

  • 有序性指的是程序按照代码的先后顺序执行。
  • 编译器为了优化性能,有时候会改变程序中语句的先后顺序,可能导致意想不到的 Bug。
  • 在 Java 领域一个经典的案例就是利用双重检查创建单例对象:
    public class Singleton {
    	static Singleton instance;
    	static Singleton getInstance(){
    		if (instance == null) {
    			synchronized(Singleton.class) {
    				if (instance == null) {
    					instance = new Singleton();
    				}		
    			}
    		}
    		return instance;
    	}
    }
    
    • 我们假设线程 A 先执行 getInstance() 方法,当执行完指令 2 时恰好发生了线程切换,切换到了线程 B 上;
    • 如果此时线程 B 也执行 getInstance() 方法,那么线程 B 在执行第一个判断时会发现 instance != null ,所以直接返回 instance,而此时的 instance 是没有初始化过的,如果我们这个时候访问 instance 的成员变量就可能触发空指针异常。
      《Java并发编程实战》课程笔记(二)

文章来源地址https://www.toymoban.com/news/detail-459520.html

到了这里,关于《Java并发编程实战》课程笔记(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JUC并发编程学习笔记(十九)原子引用

    带版本号的原子操作! 解决ABA问题,引入原子引用(乐观锁思想) AtomicStampedReference类解决ABA问题 所有相同类型的包装类对象之间值的比较全部使用equals方法比较 Integer使用了对象缓存机制,默认范围是-128至127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为v

    2024年02月05日
    浏览(41)
  • 《C++并发编程实战》读书笔记(3):并发操作的同步

    当线程需要等待特定事件发生、或是某个条件成立时,可以使用条件变量 std::condition_variable ,它在标准库头文件 condition_variable 内声明。 wait() 会先在内部调用lambda函数判断条件是否成立,若条件成立则 wait() 返回,否则解锁互斥并让当前线程进入等待状态。当其它线程调用

    2024年02月10日
    浏览(26)
  • 【JavaEE】并发编程(多线程)线程安全问题&内存可见性&指令重排序

    目录 第一个问题:什么是线程安全问题? 第二个问题:为什么会出现线程安全问题?  第三个问题:如何解决多线程安全问题?  第四个问题:产生线程不安全的原因有哪些?  第五个问题:内存可见性问题及解决方案  第六个问题:指令重排序问题? 线程安全就是多线程

    2024年02月01日
    浏览(53)
  • 《C++并发编程实战》读书笔记(1):线程管控

    包含头文件 thread 后,通过构建 std::thread 对象启动线程,任何可调用类型都适用于 std::thread 。 启动线程后,需要明确是等待它结束、还是任由它独自运行: 调用成员函数 join() 会先等待线程结束,然后隶属于该线程的任何存储空间都会被清除, std::thread 对象不再关联到已结

    2024年02月10日
    浏览(30)
  • Java并发编程实战

    2023年06月19日
    浏览(39)
  • 并发编程-JUC-原子类

    JUC 整体概览 原子类 基本类型-使用原子的方式更新基本类型 AtomicInteger:整形原子类 AtomicLong:长整型原子类 AtomicBoolean :布尔型原子类 引用类型 AtomicReference:引用类型原子类 AtomicStampedReference:原子更新引用类型里的字段原子类 AtomicMarkableReference :原子更新带有标记位的引

    2024年02月21日
    浏览(31)
  • 并发编程08:原子操作类

    Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的。在我们这里 Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。 AtomicInteger :整型原子类 At

    2024年02月04日
    浏览(30)
  • JUC并发编程之原子类

    目录 1. 什么是原子操作 1.1 原子类的作用 1.2 原子类的常见操作 原子类的使用注意事项 并发编程是现代计算机应用中不可或缺的一部分,而在并发编程中,处理共享资源的并发访问是一个重要的问题。为了避免多线程访问共享资源时出现竞态条件(Race Condition)等问题,J

    2024年02月13日
    浏览(36)
  • 并发编程学习(十):共享模式无锁、原子整数、原子引用类型

            获取共享变量时,为了保证该变量的可见性,需要使用volatile修饰。         它可以用来修饰成员变量和静态成员变量,它可以避免线程从自己的工作缓存中查找变量,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存,即一个线程对volatile变量的

    2024年02月04日
    浏览(27)
  • 《C++并发编程实战》读书笔记(2):线程间共享数据

    在C++中,我们通过构造 std::mutex 的实例来创建互斥量,调用成员函数 lock() 对其加锁,调用 unlock() 解锁。但通常更推荐的做法是使用标准库提供的类模板 std::lock_guard ,它针对互斥量实现了RAII手法:在构造时给互斥量加锁,析构时解锁。两个类都在头文件 mutex 里声明。 假设

    2024年02月10日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包