多线程1~4笔记总结

  • 并发编程三要素
  • 原子性:即一个不可再被分割的颗粒。在Java中原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。
  • 有序性:程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
  • 可见性:当多个线程访问同一个变量时,如果其中一个线程对其作了修改,其他线程能立即获 取到最新的值。
  • 悲观锁与乐观锁
  • 悲观锁:每次操作都会加锁,会造成线程阻塞。
  • 乐观锁:每次操作不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止,不会造成线程阻塞。
  • Thread.States类中定义线程的状态如下:
  • NEW:Thread对象已经创建,但是还没有开始执行。
  • RUNNABLE:Thread对象正在Java虚拟机中运行。
  • BLOCKED : Thread对象正在等待锁定。
  • WAITING:Thread 对象正在等待另一个线程的动作。
  • TIME_WAITING:Thread对象正在等待另一个线程的操作,但是有时间限制。
  • TERMINATED:Thread对象已经完成了执行。
    在这里插入图片描述
  • 线程的状态
  • 创建状态:准备好了一个多线程的对象,叫做Treade t = new Treade()
  • 就绪状态:调用了start()方法,等待CPU进行调度
  • 运行状态:执行run()方法
  • 阻塞状态:暂时停止执行,可能将资源交给其它线程使用
  • 终止状态(死亡状态):线程执行完毕(run方法结束),不再使用了
    在这里插入图片描述
  • 同步代码块和同步方法有什么区别?
  • 同步方法使用的锁是固定的this ;同步代码块使用的锁可以是任意对象
  • 如果同步方法被static修饰了,使用的锁就不是this了,而是字节码文件对象(字节码文件对象:类名.class)因为静态方法和类同时加载,此时没有创建类的对象(没有new),只有Class对象(Ticket.class)
  • wait()和notify()
  • wait()会让线程处于等待状态,其实就是把线程临时存储到等待池中。
  • notify()会唤醒等待池中的任意一个等待的线程,唤醒之后就具备了执行资格。
  • notifyAll()会唤醒等待池中所以的等待的线程
  • 唤醒之后并不能立即运行,还需要拿到同步的锁
  • 这些方法使用在同步中,因为必须要标识wait()、notify()所属的锁。
  • 同一个锁上的notify,只能唤醒该锁上被wait的线程
  • 为什么这些方法定义在Object类中?因为这些方法必须标识所属的锁,而锁可以是任意对象,任意对象可以调用的方法必须是Object中的方法
  • sleep() 和 wait() 的异同点?

相同点:可以让线程处于冻结状态
不同点:

  • sleep()必须指定时间,wait()可以指定时间,也可以不指定时间
  • sleep()不一定非要定义在同步中;wait()必须定义在同步中
  • 类的不同:sleep() 来自 Thread,wait() 来自 Object。
  • 释放锁:sleep() 不释放锁;wait() 释放锁。
  • 用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()直接唤醒。
  • JMM(Java内存模型)

线程之间的共享变量存在主内存中,每个线程都有⼀个私有的本地内存,存储了该线程以读、写共享变量的副本。本地内存是Java内存模型的⼀个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器等。
在这里插入图片描述从图中可以看出:

  1. 所有的共享变量都存在主内存中。
  2. 每个线程都保存了⼀份该线程使⽤到的共享变量的副本。
  3. 如果线程A与线程B之间要通信的话,必须经历下⾯2个步骤:
    1. 线程A将本地内存A中更新过的共享变量刷新到主内存中去。
    2. 线程B到主内存中去读取线程A之前已经更新过的共享变量。
  • 所以,线程A⽆法直接访问线程B的⼯作内存,线程间通信必须经过主内存。

  • 注意,根据JMM的规定,线程对共享变量的所有操作都必须在⾃⼰的本地内存中进⾏,不能直接从主内存中读取。

  • 所以线程B并不是直接去主内存中读取共享变量的值,⽽是先在本地内存B中找到这个共享变量,发现这个共享变量已经被更新了,然后本地内存B去主内存中读取这个共享变量的新值,并拷⻉到本地内存B中,最后线程B再读取本地内存B中的新值。

  • 那么怎么知道这个共享变量的被其他线程更新了呢?

  • 这就是JMM的功劳了,也是JMM存在的必要性之⼀。JMM通过控制主内存与每个线程的本地内存之间的交互,来提供内存可⻅性保证。

  • JMM和Java运⾏时内存区域的划分,这两者既有差别⼜有联系:

  • 区别:

两者是不同的概念层次。JMM是抽象的,他是⽤来描述⼀组规则,通过这个规则来控制各个变量的访问⽅式,围绕原⼦性、有序性、可⻅性等展开的。⽽Java运⾏时内存的划分是具体的,是JVM运⾏Java程序时必要的内存划分。

  • 联系

都存在私有数据区域和共享数据区域。⼀般来说,JMM中的主内存属于共享数据区域,他是包含了堆和⽅法区;同样,JMM中的本地内存属于私有数据区域,包含了程序计数器、本地⽅法栈、虚拟机栈。

  • 内存可⻅性
  • JMM有⼀个主内存,每个线程有⾃⼰私有的⼯作内存,⼯作内存中保存了⼀些变量在主内存的拷⻉。
  • 内存可⻅性,指的是线程之间的可⻅性,当⼀个线程修改了共享变量时,另⼀个线程可以读取到这个修改后的值。
  • 重排序

为优化程序性能,对原有的指令执⾏顺序进⾏优化重新排序。重排序可能发⽣在多个阶段,⽐如编译重排序、CPU重排序等。

  • happens-before规则

是⼀个给程序员使⽤的规则,只要程序员在写代码的时候遵循happens-before规则,JVM就能保证指令在多线程之间的顺序性符合程序员的预期。

  • happens-before关系的定义如下:
  1. 如果⼀个操作happens-before另⼀个操作,那么第⼀个操作的执⾏结果将对第⼆个操作可⻅,⽽且第⼀个操作的执⾏顺序排在第⼆个操作之前。
  2. 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执⾏。如果重排序之后的执⾏结果,与按happens-before关系来执⾏的结果⼀致,那么JMM也允许这样的重排序。
  • happen-before规则总结
  1. 单线程中的每个操作,happen-before于该线程中任意后续操作。
  2. 对volatile变量的写,happen-before于后续对这个变量的读。
  3. 对synchronized的解锁,happen-before于后续对这个锁的加锁。
  4. 对final变量的写,happen-before于final域对象的读,happen-before于后续对final变量的 读。

四个基本规则再加上happen-before的传递性,就构成JMM对开发者的整个承诺。在这个承诺以外的部分,程序都可能被重排序,都需要开发者小心地处理内存可见性问题。

  • 缓存一致性问题

java i = i + 1;

  • 可能存在下面一种情况:初始时,两个线程分别读取i的值存入各自所在的CPU的高速缓存当中,然后线程1进行加1操作,然后把i的最新值1写入到内存。此时线程2的高速缓存当中i的值还是0,进行加1操作之后,i的值为1,然后线程2把i的值写入内存。

  • 最终结果i的值是1,而不是2。这就是著名的缓存一致性问题。通常称这种被多个线程访问的变量为共享变量。

  • 也就是说,如果一个变量在多个CPU中都存在缓存(一般在多线程编程时才会出现),那么就可能存在缓存不一致的问题。

  • volatile的内存语义

在Java中,volatile关键字有特殊的内存语义。volatile主要有以下两个功能:

  • 保证可见性;
  • 64位写入的原子性,不保证复合操作的原子性;(在Java中,对基本数据类型的变量和赋值操作都是原子性操作;)
  • 禁止指令重排。

所谓内存可⻅性,指的是当⼀个线程对 volatile 修饰的变量进⾏写操作时,JMM会⽴即把该线程对应的本地内存中的共享变量的值刷新到主内存;当⼀个线程对 volatile 修饰的变量进⾏读操作时,JMM会把⽴即该线程对应的本地内存置为⽆效,从主内存中读取共享变量的值。

  • 说说 synchronized 关键字和 volatile 关键字的区别?
  • volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。
  • volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。

volatile仅仅保证对单个volatile变量的读/写具有原⼦性,⽽锁可以保证整个临界区代码的执⾏具有原⼦性。所以在功能上,锁⽐volatile更强⼤;在性能上,volatile更有优势。

  • volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。

在保证内存可⻅性这⼀点上,volatile有着与锁相同的内存语义,所以可以作为⼀个“轻量级”的锁来使⽤。

热门文章

暂无图片
编程学习 ·

C语言二分查找详解

二分查找是一种知名度很高的查找算法,在对有序数列进行查找时效率远高于传统的顺序查找。 下面这张动图对比了二者的效率差距。 二分查找的基本思想就是通过把目标数和当前数列的中间数进行比较,从而确定目标数是在中间数的左边还是右边,将查…
暂无图片
编程学习 ·

GMX 命令分类列表

建模和计算操作命令: 1.1 . 创建拓扑与坐标文件 gmx editconf - 编辑模拟盒子以及写入子组(subgroups) gmx protonate - 结构质子化 gmx x2top - 根据坐标生成原始拓扑文件 gmx solvate - 体系溶剂化 gmx insert-molecules - 将分子插入已有空位 gmx genconf - 增加…
暂无图片
编程学习 ·

一文高效回顾研究生课程《数值分析》重点

数值分析这门课的本质就是用离散的已知点去估计整体,就是由黑盒子产生的结果去估计这个黑盒子。在数学里这个黑盒子就是一个函数嘛,这门课会介绍许多方法去利用离散点最大化地逼近这个函数,甚至它的导数、积分,甚至微分方程的解。…
暂无图片
编程学习 ·

在职阿里5年,一个28岁女软测工程师的心声

简单的先说一下,坐标杭州,14届本科毕业,算上年前在阿里巴巴的面试,一共有面试了有6家公司(因为不想请假,因此只是每个晚上去其他公司面试,所以面试的公司比较少) ​ 编辑切换为居中…
暂无图片
编程学习 ·

字符串左旋c语言

目录 题目: 解题思路: 第一步: 第二步: 第三步: 总代码: 题目: 实现一个函数,可以左旋字符串中的k个字符。 例如: ABCD左旋一个字符得到BCDA ABCD左旋两个字符…
暂无图片
编程学习 ·

设计模式--观察者模式笔记

模式的定义与特点 观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式&#xf…
暂无图片
编程学习 ·

睡觉突然身体动不了,什么是睡眠痽痪症

很多朋友可能有这样的体验,睡觉过程中突然意识清醒,身体却动弹不了。这时候感觉非常恐怖,希望旁边有一个人推自己一下。阳光以前也经常会碰到这样的情况,一年有一百多次,那时候很害怕晚上到来,睡觉了就会出…
暂无图片
编程学习 ·

深入理解C++智能指针——浅析MSVC源码

文章目录unique_ptrshared_ptr 与 weak_ptrstd::bad_weak_ptr 异常std::enable_shared_from_thisunique_ptr unique_ptr 是一个只移型别(move-only type,只移型别还有std::mutex等)。 结合一下工厂模式,看看其基本用法&#xff…
暂无图片
编程学习 ·

@TableField(exist = false)

TableField(exist false) //申明此字段不在数据库存在,但代码中需要用到它,通知Mybatis-plus在做写库操作是忽略它。,.
暂无图片
编程学习 ·

Java Web day15

第十二章文件上传和下载 一、如何实现文件上传 要实现Web开发中的文件上传功能,通常需要完成两步操作:一.是在Web页面中添加上传输入项;二是在Servlet中读取上传文件的数据,并保存到本地硬盘中。 需要使用一个Apache组织提供一个…
暂无图片
编程学习 ·

【51nod 2478】【单调栈】【前缀和】小b接水

小b接水题目解题思路Code51nod 2478 小b接水 题目 输入样例 12 0 1 0 2 1 0 1 3 2 1 2 1输出样例 6解题思路 可以发现最后能拦住水的都是向两边递减高度(?) 不管两个高积木之间的的积木是怎样乱七八糟的高度,最后能用来装水的…
暂无图片
编程学习 ·

花了大半天写了一个UVC扩展单元调试工具

基于DIRECTSHOW 实现的,用的是MFC VS2019. 详见:http://www.usbzh.com/article/detail-761.html 获取方法 加QQ群:952873936,然后在群文件\USB调试工具&测试软件\UVCXU-V1.0(UVC扩展单元调试工具-USB中文网官方版).exe USB中文网 USB中文…
暂无图片
编程学习 ·

贪心(一):区间问题、Huffman树

区间问题 例题一:区间选点 给定 N 个闭区间 [ai,bi]请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。 输出选择的点的最小数量。 位于区间端点上的点也算作区间内。 输入格式 第一行包含整数 N,表示区间数。 接下来 …
暂无图片
编程学习 ·

C语言练习实例——费氏数列

目录 题目 解法 输出结果 题目 Fibonacci为1200年代的欧洲数学家,在他的着作中曾经提到:「若有一只免子每个月生一只小免子,一个月后小免子也开始生产。起初只有一只免子,一个月后就有两只免子,二个月后有三只免子…
暂无图片
编程学习 ·

Android开发(2): Android 资源

个人笔记整理 Android 资源 Android中的资源,一般分为两类: 系统内置资源:Android SDK中所提供的已经定义好的资源,用户可以直接拿来使用。 用户自定义资源:用户自己定义或引入的,只适用于当前应用的资源…
暂无图片
编程学习 ·

零基础如何在短时间内拿到算法offer

​算法工程师是利用算法处理事物的职业 算法(Algorithm)是一系列解决问题的清晰指令,也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。 如果一个算法有缺陷,或不适合于某个问题,执…
暂无图片
编程学习 ·

人工智能:知识图谱实战总结

人工智能python,NLP,知识图谱,机器学习,深度学习人工智能:知识图谱实战前言一、实体建模工具Protegepython,NLP,知识图谱,机器学习,深度学习 人工智能:知识图…
暂无图片
编程学习 ·

【无标题】

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…