文档章节

线程安全性

hackQun
 hackQun
发布于 2015/08/31 22:19
字数 1414
阅读 31
收藏 0

概念引入

         在构建稳健的并发程序时,必须正确地使用线程和锁。但这些终归只是一些机制。要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的(Shared)和可变的(Mutable)状态的访问。

         从非正式的意义使来说,对象的状态是指存储在状态变量(如实例或静态域)中的数据。对象的状态可能包括其他依赖发对象的域。例如,某个HashMap的状态不仅存储在HaspMap对象本身,还存储在许多Map.Entry对象中。在对象的状态包含了任何可能影响其外部可见行为的数据。

         “共享意味着变量可以由多个线程同时访问,而可变则意味着变量的值在其生命周期内可以发生变化。一个对象是否需要是线程安全的,取决于它是否被多线程访问。这指的是在程序中访问对象的方式,而不是对象要实现的功能。要是对象是线程安全的,需要采用同步机制来协同对对象可变状态的访问。如果无法实现协同,那么可能会导致数据破坏以及其他不该出现的结果。

         如果当多个线程访问同一个可变的状态变量时没有使用适合的同步,那么程序就会出现错误。有三种方式可以修复这个问题:
                   1 不在线程间共享该状态变量

                   2 将状态变量修改为不可变的变量

                   3 在访问状态变量时使用同步

面向对象技术不佳有助于编写出借给优雅、可维护性高的类,还有助于编写出线程安全的类。访问某个变量的代码越少,就越容易确保对变量的所有访问都实现正确同步,同时也更容易找出变量在哪些条件下被访问。Java语言并没有强制要求将状态都封装到类中,开发人员完全可以将状态保存在某个公开的域(甚至公开的静态域)中,或者提供一个对内部对象的公开引用。然而,程序状态的封装性越好,就越容易实现程序的线程安全性,并且代码的维护人员也越容易保持这种方式。

在某些情况中,良好的面向对象设计技术与实际情况的需求不一致时,可能需要牺牲一些良好的设计原则,以换取性能或者对遗留代码的向后兼容。在编写并发程序时,一种正确的编程方法就是:首先是代码正确运行,然后再提高代码的速度。由于并发错误是非常难以重现和调试的,因此如果只是在某段很少执行的代码路径上获得了性能提升,那么很可能被程序运行时存在的失败风险而抵消。

什么是线程安全性

         在线程安全性中,最核心的概念就是正确性。如果对线程安全性的定义是模糊的,那么就是因为缺乏对正确性的清晰定义。

         正确性的含义是,某个类的行为与其规范完全一致。在良好的规范中通常会定义各种不变性条件(Invariant)来约束对象的状态。以及定义各种后验条件(Postcondition)来描述对象操作的结果。由于我们通常不会为类编写详细的规范,那么如何知道这些类是否正确呢?我们无法知道,但这并不妨碍我们在确信类的代码能工作后使用它们。这种“代码可信性非常接近我们对正确性的理解,因此我们可以将单线程的正确性近似定义为所见即所知。在对正确性给出了一个较为清晰的定义后,就可以定义线程安全性:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称为这个类时线程安全的。

示例:一个无状态的Servlet

         通常,线程安全性的需求并非来源于对线程的直接使用,而是使用像Servlet这样的框架。

程序清单:

@ThreadSafe

public class StatelessFactorizer implements Servlet{

         public void service(ServlrtRequest req,ServletResponse resp){

                   BigInteger i=extractFromRequest(req);

                   BigInteger[] factors=factors(i);

                   encodeIntoResponse(resp, factors);

         }

}

与大多数Servlet相同,StatelesssFactorizer时无状态的:它既不包含任何域,也不包任何对其他类中域的引用。计算过程中的临时状态仅存在于线程栈上的局部变量中,并且只能由正在执行的线程访问。访问StatlesssFactorizer的线程不会影响另一个访问同一个StatelesssFactorizer的线程的计算结果,因为这两个线程并没有共享状态,就好像他们都在访问不同是实例。由于线程访问无状态对象的行为不会影响其他线程中操作的正确性,因此无状态对象是线程安全的。大多数Servlet都是无状态的,从而极大地降低了在实现Servlet线程安全性时的复杂度。只有当Servlet在处理请求时需要保存一些信息,线程安全性才会成为一个问题。

 

© 著作权归作者所有

下一篇: JQuery之AJAX
hackQun
粉丝 1
博文 3
码字总数 7267
作品 0
济南
程序员
私信 提问
13-从Java字节码的角度看线程安全性问题

本讲就来了解线程安全性问题,线程安全性问题是一个非常复杂,就是说,在没有充足的同步的情况下,多个线程中的操作的执行顺序是不可预测的,那么,可能就会,我们说在单线程中正常执行的问题...

G_66_hero
01/02
0
0
java并发编程与线程安全

什么是线程安全 如果对象的状态变量(对象的实例域、静态域)具有可变性,那么当该对象被多个线程共享时就的考虑线程安全性的问题,否则很可能出现与预期不同的结果。 线程安全是指:当多个线...

e良师益友
2016/01/19
108
0
Java并发编程学习笔记(一)线程安全性 1

什么是线程安全性: 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的和可变的状态的访问。“共享”意味着变量可以由多个线程同时访问,而“可变”则意味着变量的...

ponpon_
2014/05/17
0
0
线程安全与非线程安全-个人理解

类的某个方法是线程安全的, 说明这个方法在并发执行中,从开始执行到执行完毕,都是同步的, 比如:之前做的并发数据导出,并发的查询数据库, 但是在写入excel的时候,需要做一个同步,因为...

Java搬砖工程师
2018/12/17
19
0
java并发编程(三): 对象的组合

对象的组合: 如何将现有的线程安全组件,组合成我们想要的更大规模的程序。 设计线程安全的类: 设计线程安全类的三个要素: 1.找出构成对象状态的所有变量; 2.找出约束状态变量的不变性条...

ihaolin
2014/03/24
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

Rust:最小化窗口后 CPU占用率高 (winit,glutin,imgui-rust)

最近试着用 imgui-rust 绘制界面,发现窗口最小化后CPU占用会增大。 查询的资料如下: https://github.com/rust-windowing/winit/issues/783 https://github.com/ocornut/imgui/issues/1151 ...

reter
19分钟前
6
0
cloud-zuul路由网关

九、zuul路由网关 概述 1.1 能干嘛 路由、过滤 路由基本配置 路由访问映射规则 十、springCloud config分布式配置中心

榴莲黑芝麻糊
19分钟前
6
0
Circuit Breaker模式

Circuit Breaker模式会处理一些需要一定时间来重连远程服务和远端资源的错误。该模式可以提高一个应用的稳定性和弹性。 问题 在类似于云的分布式环境中,当一个应用需要执行一些访问远程资源...

mskk
32分钟前
9
0
写论文之前的准备都有哪些?干货来了!

原文链接:https://www.lwfdy.com/archives/144.html 之前跟大家谈了许多有关于初稿修改以及写作事项需要注意的问题,那么今天我们来说一说,在写之前,我们需要做哪些准备呢,为了做到下笔如...

辅导员
38分钟前
7
0
idea快捷键

Alt + Enter 引入类 Ctrl + O 查看我们继承的类或者接口中的方法,以及我们要实现的方法 Ctrl + Alt + b 查看接口实现类中方法(就是我们使用接口编程时,在调用实现类方法处直接Ctrl+鼠标左...

行者终成事
46分钟前
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部