Java线程安全与锁优化

Java共享数据的分类

  • 不可变: 不可变数据是绝对线程安全的
  • 绝对线程安全: “不管运行时环境如何,调用者都不需要任何额外的同步措施”
  • 相对线程安全: 对一个对象单独对操作是线程安全对
  • 线程兼容: 本身并非线程安全,但我们可以在调用端使用同步手段来确保在并发环境中可以安全得使用
  • 线程对立: 对象在调用端无论使用何种同步手段,都无法确保安全

线程安全的实现方法

  • 互斥同步: 使用互斥量
  • 非阻塞同步 : 使用原子操作

锁优化

  • 自旋锁与自适应自旋锁: 使用多个忙循环而不是挂起,当忙循环超过某个固定阈值的时候挂起,自适应指得是动态选择阈值
  • 锁清除: 消除不必要的锁
  • 锁粗化: 扩大锁的范围,避免在循环中频繁加锁
  • 轻量级锁: 使用CAS操作,避免互斥加锁,若CAS操作失败,则使用互斥加锁
  • 偏向锁: 让第一个使用对象对线程持有,在第二个线程到来前,不进行任何同步操作。

Java内存模型与线程

硬件间速度的差距

因为计算机各种硬件之间速度的差距实在是太大了,这严重地影响了计算机的整体效率,很多时候软件并不能够充分地利用计算机的资源,让处理器去等待内存,一种解决方案就是在内存和处理器之间引入一个缓存,来尽量减轻这个速度的差距。在多处理器系统中,往往对应着多个缓存。

缓存一致性

往往这些缓存都储存着和内存一样的数据,他们互为拷贝,我们必须保证他们的数据是同步修改的。这有很多种协议来维护。

乱序执行

为了更好的利用处理器的运算单元,处理器可能会对输入的代码片段进行乱序执行优化,Java虚拟机也是如此。

Java内存模型

Java内存模型中有两种内存,第一种是一个主内存,第二种是多个线程工作内存,线程私有。

内存间的互相操作

Java内存模型有8种原子操作
lock:作用与主内存中的变量,让其变为线程独占
unlock:和lock相反
read:把主内存中的变量传输到工作内存,准备load
load:把read的值放入线程工作内存的变量副本中
use:把线程工作内存中的值拿到执行引擎中
assign:从执行引擎中获得值写入工作内存
store:把工作内存中的变量传输到主内存,准备write
write:把store得到的值写入主内存。
这些操作的相互执行关系很复杂,但都能推导出,这里不赘述
long和double是64位数据,Java内存模型不强制但推荐把他们也实现为原子操作

volatile型变量

volatile类型有两种特点,第一是保证对所有线程的可见行,即所有线程使用其前都要在工作内存中刷新他的值,但是这些操作并非原子性,所以不能保证并发安全。第二是禁止语义重排。他前面的代码不能排到他后面去,他后面的代码不能重排到他前面。

Java线程调度

Java有10种优先级,但是操作系统却不一定是10种,若<10则导致Java线程某些级别无差距,若>10则导致有些系统线程优先级无法使用。

Java线程状态

新建、运行、无限期等待(不能主动苏醒,能被唤醒)、期限等待、阻塞、结束

Java内存区域于内存溢出异常

Java运行时的数据区域

方法区、虚拟机栈、本地方法栈、堆、程序计数器

程序计数器

线程私有
为了支持多线程,Java虚拟机为每个线程设置独立的程序计数器,各条线程之间计数器互不影响,独立储存。
如果线程执行的是Java方法,计数器指向正在执行的字节码指令地址,如果线程执行的是Native方法,这个计数器则为空
程序计数器区域是唯一一个没有任何OutOfMemoryError情况的区域。

Java虚拟机栈

线程私有
每个方法在执行的同时都会向虚拟机栈申请一个栈帧用于储存局部变量表、操作数栈、动态链接、方法出口等信息,每个方法的调用直到执行完成,对应一个栈帧在虚拟机栈中入栈到出栈的过程,局部变量表存放了方法执行过程中的所有局部变量,编译期间就确定了。所以,这个栈帧的大小是完全固定的。
虚拟机栈会有StackOverflowError和OutOfMemoryError,前者在固定长度的虚拟机栈中出现,后者在变长虚拟机栈中出现。这要看虚拟机是哪一种虚拟机。

本地方法栈

类似于Java方法使用Java虚拟机的栈,这一区域是本地方法使用的栈。
有StackOverflowError和OutOfMemoryError。

Java堆

线程共享
此内存唯一目的是存放对象实例,Java虚拟机规范中描述到:所有对象实例以及数组都要在堆上分配。但是随着JIT编译器等技术等发展,所有对象都分配在堆上也渐渐不那么绝对了。
Java堆允许不连续
Java堆有OutOfMemoryError

方法区

线程共享
储存虚拟机加载的类信息,常量,静态变量,即时编译后的代码等数据
可以选择不进行垃圾回收,但这不明智
有OutOfMemoryError

运行时常量池

是方法区的一部分
允许在程序运行时创建常量
有OutOfMemoryError

直接内存

不是虚拟机的一部分,在NIO类中,允许Native函数库向操作系统申请内存,提供一些方式访问,使得在一些场景提高性能,
有OutOfMemoryError

HotSpot VM

对象的创建

当虚拟机碰到new的时候,会先检查类是否被加载、解析、初始化,如果没有,则先执行相应的加载过程,当类检查通过以后,会为对象分配内存,这个内存大小是完全确定的,虚拟机会从虚拟机堆中分配这块内存并将这块内存初始化为0,然后执行init方法。
因为Java需要支持多线程,所以这里实际需要同步处理,还有一种解决方案叫做TLAB(Thread Local Allocation Buffer)预先为每个线程分配一小块内存,就不会受到多线程影响,当TLAB用完以后,需要分配新的TLAB,这时才需要同步锁定,在TLAB分配时即可提前初始化这块内存为0,当然也可以不提前。

对象的内存布局

内存中储存的布局可以分为3块区域: 对象头、实例数据和对齐填充。
对象头分为两部分,第一部分储存了哈希码、GC分代年龄、锁状态、线程持有锁、偏向线程ID等等这部分在32位机器上为32bit,在64位机器上为64bit,第二部分可有可无,储存类型指针,指向类元数据。另外对于Java数组,就还有第三部分用于记录数组长度。
对齐填充就是为了让对象大小变成8字节的整数倍

对象的访问定位

Java程序通过栈上的reference数据来操作堆上的具体对象,主流的对象访问方式有两种,第一种是句柄访问,Java堆会划分出一块内存用作句柄池,reference储存句柄地址,句柄储存对象实例和类型各自的具体地址;第二种是直接访问,这种情况Java对象的布局中就要考虑储存类型数据,reference就储存对象的直接地址。前者在垃圾收集时移动时快,后者访问速度快。

走进Java

what’s that

这是学习《深入理解Java虚拟机》周志明 著. 的笔记

Java 的优点

结构严谨、面向对象、脱平台、相对安全的内存管理和访问机制、热点代码检测和运行时编译及优化、拥有完善的应用程序接口及第三方类库……

JDK,JRE,Java SE API 的区别


图片来源于《深入理解Java虚拟机》

Java平台

Java Card: 支持一些Java小程序运行在校内次设备(智能卡)上的平台
Java ME: 支持Java程序运行在移动终端上的平台,对Java API所精简
Java SE: 支持面向桌面级应用的平台,有完整的Java核心API
Java EE:支持多层架构的企业应用的平台,出Java核心API外还做了大量补充

Sun HotSpot VM

这是Sun JDK和OpenJDK中所带的虚拟机

EulerTourTree

Euler Tour Tree

任何一颗树都能够用欧拉旅行路径来表示,欧拉旅行路径是一个序列,他记录了一颗树的dfs的时候的顺序,记录了进入每个节点的时间和退出该节点的时间,这样以后,子树就成了ETT上连续的区间
,当我们对子树进行交换的时候,我们就可以将这个区间平移。这里我们用splay维护即可

kernelFunctions

kernel functions

核函数是一个函数,他能够把低纬空间映射到高维空间,他的输入是低维空间的两个点,他的输出是这两个点在高维空间上的内积。

why kernel functions

某些在低维空间无法使用超平面分割的点集,他们被某些函数映射到高维空间以后,能够被超平面分割。并且在高维空间中计算他们的内积很容易(就是核函数)

应用

Simple Example: x = (x1, x2, x3); y = (y1, y2, y3). Then for the function f(x) = (x1x1, x1x2, x1x3, x2x1, x2x2, x2x3, x3x1, x3x2, x3x3), the kernel is K(x, y ) = (<x, y>)².
Let’s plug in some numbers to make this more intuitive: suppose x = (1, 2, 3); y = (4, 5, 6). Then:
f(x) = (1, 2, 3, 2, 4, 6, 3, 6, 9)
f(y) = (16, 20, 24, 20, 25, 30, 24, 30, 36)
<f(x), f(y)> = 16 + 40 + 72 + 40 + 100+ 180 + 72 + 180 + 324 = 1024
A lot of algebra, mainly because f is a mapping from 3-dimensional to 9 dimensional space.
Now let us use the kernel instead:
K(x, y) = (4 + 10 + 18 ) ^2 = 32² = 1024
Same result, but this calculation is so much easier.

metric_space

metric space

度量空间metric space是一个度量metric的集合,通常度量metric是定义在某点集上的函数,

metric

度量必须满足下面四个条件

the distance from a point to itself is zero
the distance between two distict point is positive
the distance from A to B is the same as the distance from B to A
the distance from A to B(directly) is less than or equal to the distance from A to B via any third point C

表达

通常我们使用有序对(M,d)来表示度量空间,M是集合,d是集合M上的度量

代更

参考wikipedia

软件需求复习

软件工程与软件需求

什么是软件 软件是使硬件充分、自动、智能化地发挥作用的纽带
软件是用户和计算机硬件之间的接口和桥梁
软件开发的目标是什么 为用户提供满意的软件产品或服务
什么是需求 需求是在一定的时期,在一既定的价格水平下,消费者愿意并且能够购买的商品数量
软件开发瀑布模型 需求分析、设计、编码、测试、维护
软件项目成功的主要因素 *用户的参与
执行层的支持
*清晰的需求描述
合理的规划
*现实的客户期望
软件项目失败的主要因素 *不完整的需求
*缺乏用户参与
资源不足
*不切实际的用户期望
缺乏执行层的支持
*需求变更频繁
规划不足
*提供了不再需要的
缺乏IT管理
技术能力不足
需求工程包括那两个部分 需求开发和需求管理
需求开发包括哪些基本过程 业务需求定义、需求获取、需求分析与建模、需求描述、需求验证

需求定义

什么是业务需求定义 明确需求目标
界定需求范围
业务需求定义与那个层次的需求相关 定义业务需求
SMART原则 s=specific 明确的
m=measurable 可衡量的
a=attainable 可达到的
r=relevant/realistic 相关的/现实的
t=timebased/timebound 有时间期限的
问题分析五步法是哪五步 问题定义达成共识
分析问题,理解根本原因
确定相关人员或用户
定义解决方案的界限
确定加在解决方案上的约束
如何确定目标 找到问题 -> 利用鱼骨图和Pareto图分析 找到主因
定义需求范围三步法是哪三步 划分主题域,[使用构件图]
确定主题域范围,[使用上下文关系图]
标识业务事件与报表,[event,report]
SRS 软件需求规格说明书

需求捕获

换句话解释什么是需求捕获 需求捕获就是收集用户需求
是熟悉用户的工作场景,了解业务事件、报表和流程,进而理解用户碰到的真正的问题和障碍
需求捕获有哪些策略 主动、聚焦、破解隐藏需求、破解阻碍心理、不忽视变更、协商
需求捕获有哪些主要方法 用户访谈、用户调查、文档考古、情节串联版、现场观摩、联合开发
需求捕获的常用工具 三表一图(业务属性表、业务活动表、业务岗位角色表、业务流程图)
SERU (主题、事件、报表、用例)
任务卡片
场景说明

需求分析与建模

需求分析做什么 是业务分析
是对业务相关人员、数据、事件、报表等作全面的分解和研究
是对业务活动和流程的梳理和理解
在上诉基础上通过流程图、活动图、数据流图对业务流程进行描述,通过类图、ER图对业务实体进行描述、通过用例图对需求场景和角色进行描述、并对上诉业务流程实体场景和角色的相关内容进行细化
需求分析的第一个周期是什么 理清框架和脉络
需求分析的第一阶段包括哪三个方面的分析 业务流程分析,业务实体分析,场景和角色分析
流程一般分为哪三个层次,一般有哪三种类型 三个层次:组织级、部门级、岗位级别
三种类型:生产流程、管理流程、支持流程
业务流程分析的产物包括哪三种图 跨职责流程图、活动图、数据流图
什么是业务实体分析 业务实体分析是找出业务相关的数据、报表、术语,以及他们之间的关系。
业务实体分析与业务流程分析有什么区别 流程分析是识别出各种活动的顺序或步骤
业务实体分析是识别出各种活动相关的数据输入、输出或其他相关角色、概念等。
业务实体分析等产物是什么 类图、E-R图
业务实体分析过程中进行类图绘制的主要步骤包括哪几步? 标识类、确定类的属性名和方法名、标识类间的关系、标识约束和规则
角色与使用场景分析中,参与者和用例是什么关系? 参与者是系统的使用者,是系统的直接参与者,在系统外,是用例的调用者;用例是系统的组成部分,在系统内。
需求分析的第二周期与第一周期的区别在哪里?试举例说明在第二周期中需明确的需求细节。 第一周期是理清框架和脉络,第二周期是确定细节。
在第二周期中需明确的需求细节如:类成员函数的参数、属性的类型、取值范围等。
需求分析的第二个周期做什么 填充细节
流程分析的细节: 入口条件、输入、活动、输出、输出条件、活动间的依赖关系。描述方法:流程表、跨职责流程图、活动图
实体分析的细节: 对第一阶段形成的报表、类图、E-R图等的细节进行填充
场景分析的细节: 明确事件流、功能点、界面原型、规则与约束等

需求验证

需求验证的方法 形式化方法和人工技术评审

需求管理

什么是SRS,什么是需求项 SRS是软件需求规格说明书
需求项是需求文档中相对独立的功能和非功能需求描述,被唯一的编号,不同的需求项之间没有矛盾没有重叠
需求项如何划分优先级 先做WBS
业务优先判断,再做技术依赖,项目风险判断
什么是德尔菲(Delphi)法 也叫专家意见法,即应用背对背的通信方式征询专家小组成员的意见
需求管理包括哪些 基线管理、变更管理、跟踪管理
什么是版本,什么是基线 在项目开发过程中,绝大部分的配置项都要经过多次的修改才能最终确定下来。对配置项的任何修改都将产生新的版本。所以版本是某个配置项的状态标识。基线则是特定的版本,是一组配置项的集合。
需求管理的目的是什么 为了有效地控制和管理需求更改等
为什么需求跟踪要双向跟踪
需求变更影响分析从哪三个方面进行 业务影响分析、技术影响分析、项目影响分析
需求变更的技术影响分析指的是什么? 是指变更带来多大工作量的变化的分析。
需求变更的业务影响分析指的是什么? 影响的范围、影响哪些人、影响的结果这三个方面,最后得出变更的合理性、必要性、影响度方面的评价。
需求变更的项目影响分析又是指什么? 是指基于工作量分析,对整个项目在时间、进度、成本方面的影响。

珂朵莉树

珂朵莉树

珂朵莉树是一颗树,我们用集合来维护,c++中集合是红黑树,所以我们借助此集合来完成珂朵莉树。
我们将区间分段,那么各段有唯一的左端点,我们将左端点放入集合,当我们遍历集合的时候,我们就得到了我们要的序列,此时我们维护了结构,但未维护值,进一步发现我们可以使用map,用键值对来维护更多信息,键用来维护树的结构,值来维护序列的值。

split

因为我们要维护区间信息,所以我们需要操作split来提取区间,本质上提取区间为提取单点,这一点在splay中表现的很出色,当我们提取出左端点和右端点的时候,区间也就被提取出来了,如果提取位置x,在红黑树中我们二分到x的位置,若此处是一个区间[l,r],我们将此区间拆分为[l,x-1][x,r]即可。

assign

我们提取出区间,删掉这些节点然后,插入一个新的节点即可

add

我们提取出区间,暴力更新所有节点即可

sum

我们提取出区间,暴力计算所有节点,使用快速幂

kth

我们提取出区间,还是暴力

什么时候选择此数据结构

数据随机且含有区间赋值操作,此数据结构的操作可以在splay上实现,并维护更多信息,map法仅仅只是编码简单了很多。

例题

C. Willem, Chtholly and Seniorious

odt代码