成都汇智动力老师直接接听

400-029-09** 400-029-0997 转 65635
查看完整号码
扫码拨号
微信扫码拨号

Java开发之高并发必备篇(二)——线程为什么会不安全?

作者:汇智动力学院 来源:汇智动力学院 2022/10/6 14:22:17

上期我们提到了的案例中,三个窗口线程卖票出现了有窗口卖的票是...

上期我们提到了的案例中,三个窗口线程卖票出现了有窗口卖的票是一样的问题,也就是的“线程不安全问题”,这篇文章我们就来聊聊“线程为什么会出现不安全”。 1. 什么是线程安全? 线程安全*早是由Brian Goetz 在其编写的“Java Concurrency In Practice”(Java并发编程实战)中定义的,它是这样来定义的: 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步,或者在调用方代码不做其他的协调操作,这个对象的行为获取的结果仍然是正确的,那个称这个对象是线程安全的。 在我们之前的三个线程卖票的案例,多个线程访问同一个对象的ticket 数据,但是我们发现操作这个对象获取的数据有的时候出现了重复性的错误结果,所以说我们之前写的卖票案例是“线程不安全”的。 虽然知道了三个线程访问ticket 出现重复数据的现象是线程不安全的,但是不知道为什么多线程并发访问数据不安全,所以接下来我们讲围绕“为什么线程并发访问不安全”来讲解。 2.对象的有状态和无状态性 Java中按照状态可以把对象分为有状态和无状态两种; 无状态对象 (Stateless Bean):无状态对象就是没有实例变量的对象,所以也无法保存数据,它不包含域也没有引用其他类的域。又因为无状态对象没有存储的数据那么这个对象也没有什么改变之说所以是不可变的,同样的多线程下对该对象的任意操作都不会改变对象的状态。所以“ 无状态的对象一定是线程安全的 ”。定义无状态案例如下:
有状态对象 (Stateful Bean):就是有实例变量的对象,可以保存数据。
我们知道实例的数据是保存在堆中,而堆中的数据是可以被多个线程共享的(如上图); 而在多线程同时访问相同堆中的数据进行读写操作时,就达到了 竞态条件 ,导致多线程在竞争资源读写数据时最后的结果不会像我们预想的那样正确,出现线程不安全的情况。同时修改对象的数据对象的状态也被改变所以被称为有状态对象,定义案例如下:
3. 竞态条件 竞态条件是由于当一个对象或者一个不同步的共享状态,被多个线程修改时,会出现由于不恰当的执行时序而出现不正确的结果所引起。分析我们之前的卖票案例:
如果此时都在访问可共享的对象MyRunnable,如果此时“窗口1”和“窗口2”两个线程同时进入run方法并执行到了图中代码处。由于竞争下执行的代码时间线可能如下:
由图中可以看出,当“窗口1”线程获取到ticket数据的时候并判断ticket>0 的时候此时ticket为100,而“窗口2”线程此时正在输出ticket结果并且还是100,之后再“窗口1”线程输出100之后,才执行ticket--操作。所以导致两个线程开始输出ticket的值的结果都为100;我们发现在竞态条件下多线程访问的数据是“ 脏数据 ”即错误的数据。 4. 指令重排 其实除了竞态的时候会出现不恰当的执行时序外,指令重排也会导致代码执行的顺序并不是按照你书写顺序的意愿执行的。 代码运行一般步骤是这样的: 1、从主内存中获取指令解码 2、在线程内存中计算值 3、执行代码操作 4、把结果写入主内存(主内存所有线程共享) 而把结果写入主内存的操作比较耗时,CPU为了提高性能,可能不会等它完成,就进行对下一个指令解码计算,这就是指令重排了。定义如下: 指令重排: 计算机为了性能优化会对汇编指令进行重新排序,以便充分利用硬件的处理性能。 经典案例:

分析 :虽 (1)“步骤1”先于“步骤2” (2)“步骤1”和“步骤2”同时间片内执行 (3)“步骤1”后于“步骤2”执行 指令重排会虽然会改变代码执行的顺序,但是在单线程下最后执行的结果是不变的,所以在单线程下是没有什么问题的;而如果在多线程中,同时操作一个数据,如果一个读,一个写,当写的线程值已经改变了但是还没写入主内存时(也就是说值的改变其他线程还没有看到),另一个线程已经开始读取了,那么这个时候就会出现和预期不一致的结果。 其实现在对于多线程并发为什么会出现不安全的问题已经很清楚了,究其根本是因为多线程是不共享的,并且也无法准确的知道互相之间的状态,包括值的修改也无法可见才会导致修改数据出现问题,出现线程不安全的问题。 上面介绍了一个所有线程共享的主内存,那么主内存又是什么呢?线程运行的内存模型是怎么的呢?别急,下篇内容我们就介绍下线程模型和java线程的内存模型。 二、算法的设计原则   ①、正确性:首先,算法应当满足以特定的“规则说明”方式给出的需求。其次,对算法是否“正确”的理解可以有以下四个层次:   一、程序语法错误。   二、程序对于几组输入数据能够得出满足需要的结果。   三、程序对于精心选择的、典型、苛刻切带有刁难性的几组输入数据能够得出满足要求的结果。   四、程序对于一切合法的输入数据都能得到满足要求的结果。   PS:通常以第 三 层意义的正确性作为衡量一个算法是否合格的标准。   ②、可读性:算法为了人的阅读与交流,其次才是计算机执行。因此算法应该易于人的理解;另一方面,晦涩难懂的程序易于隐藏较多的错误而难以调试。   ③、健壮性:当输入的数据非法时,算法应当恰当的做出反应或进行相应处理,而不是产生莫名其妙的输出结果。并且,处理出错的方法不应是中断程序执行,而是应当返回一个表示错误或错误性质的值,以便在更高的抽象层次上进行处理。   ④、高效率与低存储量需求:通常算法效率值得是算法执行时间;存储量是指算法执行过程中所需要的*大存储空间,两者都与问题的规模有关。   前面三点 正确性,可读性和健壮性相信都好理解。对于第四点算法的执行效率和存储量,我们知道比较算法的时候,可能会说“A算法比B算法快两倍”之类的话,但实际上这种说法没有任何意义。因为当数据项个数发生变化时,A算法和B算法的效率比例也会发生变化,比如数据项增加了50%,可能A算法比B算法快三倍,但是如果数据项减少了50%,可能A算法和B算法速度一样。所以描述算法的速度必须要和数据项的个数联系起来。也就是“大O”表示法,它是一种算法复杂度的相对表示方式,这里我简单介绍一下,后面会根据具体的算法来描述。   相对(relative):你只能比较相同的事物。你不能把一个做算数乘法的算法和排序整数列表的算法进行比较。但是,比较2个算法所做的算术操作(一个做乘法,一个做加法)将会告诉你一些有意义的东西;   表示(representation):大O(用它*简单的形式)把算法间的比较简化为了一个单一变量。这个变量的选择基于观察或假设。例如,排序算法之间的对比通常是基于比较操作(比较2个结点来决定这2个结点的相对顺序)。这里面就假设了比较操作的计算开销很大。但是,如果比较操作的计算开销不大,而交换操作的计算开销很大,又会怎么样呢?这就改变了先前的比较方式;   然后我们再说说算法的存储量,包括:    程序本身所占空间;    输入数据所占空间;    辅助变量所占空间;   一个算法的效率越高越好,而存储量是越低越好。 三、算法的分类 算法可以宏泛的分为三类: 一,有限的,确定性算法 这类算法在有限的一段时间内终止。他们可能要花很长时间来执行指定的任务,但仍将在一定的时间内终止。这类算法得出的结果常取决于输入值。 二,有限的,非确定算法 这类算法在有限的时间内终止。然而,对于一个(或一些)给定的数值,算法的结果并不是的或确定的。 三,无限的算法 是那些由于没有定义终止定义条件,或定义的条件无法由输入的数据满足而不终止运行的算法。通常,无限算法的产生是由于未能确定的定义终止条件。 Java中常见的算法有: ①、排序 排序就是对一组数据按照一定的顺序(从大到小或者从小到大)进行排序; 常见排序如下: 简单排序:冒泡排序、选择排序、插入排序; 高级排序:快速排序、希尔排序、归并排序、基数排序、鸡尾酒排序等等; ②、递归 递归是一种直接或者间接调用自身的一种算法,递归的目的是简化程序设计使程序更加易读; ③、查找 在一些(有序的/无序的)数据元素中,通过一定的方法找出与给定关键字相同的数据元素就叫做查找; ④、统计 指对有关数据的搜集、整理、计算、分析、解释、表述等的活动。 往期文章 墙裂推荐 [1] Java开发之高并发必备篇(一)——线程基础 [2] Java开发篇——设计模式(5)装饰设计模式 [3] Java开发篇——设计模式(4)先生需要代购吗? 原创视频 墙裂推荐 2022转行软件测试之就业喜报 ↓↓↓

活动福利 // 1 毕业礼包 | 毕业学员免费赠送《软件测试技术大咖专题课》,助力学员早日突破高薪瓶颈 // 2 入职礼包 |就业学员免费赠送《Java语言开发视频课》及全套源代码,市场价值12800元 // 3 推荐有奖 |推荐好友成功报名,立得丰厚“伯乐”红包 (欢迎详询校区老师)

详询软件测试&开发培训事宜

添加微信咨询
杨老师 @成都汇智动力

专业解答各类课程问题、介绍师资和学校情况

微信号:186******73

立即咨询

“成都汇智动力”是成都汇智动力信息技术有限公司在教育宝平台开设的店铺,若该店铺内信息涉嫌虚假或违法,请点击这里向教育宝反馈,我们将及时进行处理。

机构评分

环境:4.5师资:4.5服务:4.0效果:4.0

公示信息

店铺名称:成都汇智动力

单位名称:成都汇智动力信息技术有限公司

账号名称:cdhzdl(180******07)

所属城市:四川成都

入驻时长:11年

在线客服:在线聊

微信咨询

返回顶部