前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发编程-什么是线程安全?

并发编程-什么是线程安全?

作者头像
ImportSource
发布2018-04-03 16:34:02
7910
发布2018-04-03 16:34:02
举报
文章被收录于专栏:ImportSourceImportSource

定义“线程安全”这个概念是一个非常复杂的事情。越是正式而严肃的描述它越是复杂难懂,不仅没办法提供一些实际的指导,而且还没法有一个直观的理解。还有一些不太正式的描述,也看起来让人比较困惑。比如你通过某搜索引擎就会搜索到很多像下面这样的一些“定义”:

…可以被多个线程调用并且线程之间不会有错误的交互

(…can be called from multiple program threads without unwanted interactions between the threads.

或者:

可以被多个线程同时调用,并且在调用者的代码中没有任何其它的操作。

(…maybe called by more than one thread at a time without requiring any other action on the caller’s part)

当你看到这些定义的时候,毫无疑问你会犯晕。这些话就像你听到类似这样的话:“如果一个类可以被多个线程安全的访问那么这个类是安全的”。你咋一听觉得是没什么问题,逻辑上也没错,但,然并卵,你这不是废话吗,并没有对我们有实际的帮助。我如何区别线程安全的类和非线程安全的类呢?进一步说,“安全”(safe)的含义究竟是什么?

任何对线程安全性的定义中,最核心的概念就是正确性(correctness)。如果我们对于线程安全性的定义是模糊的(fuzzy),那是因为我们缺少对正确性的清晰的定义。所以接下来就来讨论正确性的问题。

正确性的含义是“一个类的行为遵循它的规范”。一个好的规范会定义约束对象状态不变的内容以及会定义能够描述该对象各种操作的后果。由于我们通常不能给我们的class写出明晰的规范,那么我们如何才能尽可能的知道我们的类是正确的呢?不能,但这并不能阻止我们使用这些类的步伐,只要我们说服了自己,“the code works”,代码是没有问题的。这种代码说服(code confidence)就很接近于我们前面说的“正确性”(correctness),所以让我们只是假定说单线程的正确性就是“所见即所得”(we know it when we see it)吧。现在我们已经给正确性做了一个比较清晰的定义了,不知道你有没有get到,那么是时候来定义一下什么是“线程安全”了:当多个线程访问某个类的时候,这个类依然能持续的表现出正确行为,那么我们认为这个类就是线程安全的。也就是确保正确性。

当多个线程访问某个类时,不管runtime使用什么样的调度方式或者这些线程怎么交替执行,在调用端的代码中也没有任何额外的同步机制以及其他协同机制,在这种情况下,这个类依然能表现正确,那么我们认为这个类是线程安全的。

由于任何一个单线程的program也可以看成是一个多线程program,所以如果这个program在单线程环境下都不能表现正常,那么这个program肯定不是线程安全的。如果一个对象被正确的实现,那么无论你是调用它的public方法还是读写public fields都不会违背它的任何不变性以及后置条件(post conditions)。一个线程安全的类的instance(实例)无论是在串行和并行的情况下都应该是坚挺的。(ps:就是说一个线程安全的类在任何情况下都应该是表现正常的。)

在线程安全的类中封装了有关同步的内容,所以客户端无需再采取同步措施

2.1.1.例子:无状态Servlet。

在第一章中,我们列举了一堆框架,这些框架创建很多线程,并在这些线程中调用你写的代码,,这就要求你写的代码必须是线程安全的。通常的话,线程安全的需求并不是让我们去直接使用线程,而是使用一些像Servlets框架的时候,会有线程安全的需求。接下来我们将会开发一个简单的例子,一个基于servlet的因数分解服务(factorisation service)并且一点点的扩展它,慢慢的加功能,同时确保这个service的线程安全性。

列表2.1 向我们展示了一个简单的因数分解的servlet。这个servlet从request中解包得到数值,然后进行因数分解,然后再把结果打包塞给servlet的response返回。

列表2.1 一枚无状态Servlet

StatelessFactorizer就像大多数的servlets一样,无状态,也就是stateless:什么样的类是stateless类呢?就是没有fields,没有引用其他类的fields的类。针对于指定运算的那些transient状态都只存在于local variables。相信你是知道的,这些local variables都只存在于线程的stack 里边(thread’s stack)。也就是说这些local variables只能被正在执行的线程内部访问,这些变量是不对外共享的。一个访问StatelessFactorizer的线程不会影响到其他正在访问StatelessFactorizer的线程的运算结果。因为这两个线程没有共享状态,也就是说她们访问的是不同的instances。由于访问一个stateless的对象的线程的各种操作并不会影响到其他的线程操作的正确性,所以只要是stateless对象都一定是线程安全的。

stateless对象一定是线程安全的

事实上大多数servlets都是无状态的,这就减轻了我们确保servlet线程安全的负担。但,如果servlet们想要在处理请求时保存一些信息,这个时候线程安全性便会变成一个问题。(ps:因为一旦有了fields就意味着可能存在线程安全问题。)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-07-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ImportSource 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档