一、聊聊并发—线程安全到底在说什么

前言

并发编程的目的是为了让程序运行得更快,提高程序的响应速度,虽然我们希望通过多线程执行任务让程序运行得更快,但是同时也会面临非常多的挑战,比如像线程安全问题、线程上下文切换的问题、硬件和软件资源限制等问题,这些都是并发编程给我们带来的难题。其中线程安全问题是我们最关心的问题之一,我们接下来主要就围绕着线程安全的问题来展开。

线程安全性

首先我们要明白,要如何界定线程安全和线程不安全,我查找了很多资料,没能找到一个我认为权威又严谨的定义来界定它们,不过我觉得有一个概念可以帮助我们来区分线程安全和非安全:竞态关系。

那什么叫竞态关系呢。当多个线程同时访问同一个资源,如果这个共享资源对访问顺序敏感,程序的输出结果会严重依赖对事序的致命相依性,这个时候多个线程之间就存在竞态关系,当存在竞态关系的时候,此时程序的执行结果有很多不确定性,也就是说程序运行的结果全凭运气。

那是不是存在竞态关系的线程一定不安全,不存在竞态关系的线程之间一定安全呢?在没有任何其他约束的条件下,不添加额外加的同步,线程间存在竞态关系,那一定不是线程安全的;如果不存在竞态关系,那它们一定是线程安全的。因为竞态关系是发生在共享的资源上,如果没有竞态关系说明了不会对共享资源同时访问,也就不存在线程安全的问题了。

在《Java并发编程实战》一书中给出了线程安全的定义:当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程将如何交替执行,并且在主代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为。而且这本书第一章的开头就这样写道:要编写线程安全的代码,其核心是要对\color{#FF7D00}{可变的共享状态}操作访问的管理。其实就是为了防止共享状态在并发访问的时候发生不可控状态,所以对于在线程中共享的那些状态一定要引起我们格外的注意。

Tips:

共享的和可变的状态 这个一定要记牢,这是线程安全的核心

Java线程间消息传递方式

在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。

  • 共享内存:共享内存的模型是使用比较多的一种模型。这种通讯模型通过设置一个共享变量,多线程之间通过操作同一个变量的方式达到通讯的目的。但是这种方式就需要我们在操作共享变量的地方或者代码片段中,显式指定线程之间互斥执行。
  • 消息传递:消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信。

多线程的通信大部分是通过共享内存来进行的,在Java中多线程的通信方式也是采用共享内存,但是这种方式是有弊端的,这种通信方式其实是线程之间通过写-读内存中的公共状态来隐式进行通信,那多线程之间是如何进行公共状态的内存读写,我们没办法显式的看到,所以这就需要我们了解多线程对于内存的读写机制。

Java多线程的内存交互

Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存。线程的工作内存中保存了该线程中用到的变量的主内存副本拷贝,线程对共享变量的所有操作都必须在工作内存中进行,不能直接操作主内存变量,而是将变量拷贝到本地内存中,在本地内存操作完成以后,再将结果同步回主内存,不同的线程之间也无法直接访问对方工作内存中的变量。如下图所示:

221.png

当线程A需要向线程B发送消息时,首先A通过拷贝主内存中的变量到自己的本地内存中,在本地内存进行处理,处理完成以后,将自己本地内存中的数据同步到到主内存。线程B将线程A同步到主内存中的变量拷贝到自己的本地线程,然后完成自己的处理,如此往复的进行,线程A和线程B就完成了消息的传递。

在这里主要想说ava多线程之间是如何通过共享内存来进行消息传递的,以及多线程和共享变量之间的交互方式。这里我们就对Java内存模型混个眼熟,就先不介绍Java内存模型是什么了,避免给读者们增加负担,我会在接下来的文章中进行详细的介绍,毕竟内存模型也是并发编程中比较重要的一部分内容。

并发带来的问题

通过上面的介绍我们可能了解多线程之间通信的方式,但是这种方式也会带来两个问题:可见性和访问问题。

因为线程之间都是通过访问主内存来进行数据交换的,那假如线程A先读取了某些共享数据,之后线程B对这些数据进行了修改,那么线程A可能看不到线程B对这数据的改动。

当线程A和线程B同时对这个共享数据做出修改时,到底是A线程数据为准还是B线程数据。

除此之外呢,还会带来原子性、重排序问题,这个我们后面的文章会详细的进行介绍。

绝对的线程安全

我们之前说过,线程安全是和可变的共享变量有关系,那如果没有了共享变量或者共享变量不可变,是不是这个类就是绝对的线程安全了?答案是的。这也我们所说的不可变对象和无状态对象,这两种对象一定是线程安全的。

无状态对象

无状态对象,它既不包含任何域,也不包对其他类中域的引用,计算过程中的临时状态仅存于线程栈上,只能由当前线程访问。我们可以认为无状态变量是没有共享状态的,所以是线程安全的。Java中Servlet对象,就是一个无状态的对象。

public class ServletTest implements Servlet {
    public void init(ServletConfig servletConfig) throws ServletException { }

    public ServletConfig getServletConfig() {return null; }

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { }

    public String getServletInfo() { return null;}

    public void destroy() { }
}

不可变对象

不可变对象它只有一种状态,而且这种状态由构造函数来控制,不可变对象一旦被创建完成,就没有办法修改。既然它只有一种状态,那就不存在改变的可能,所以不可变对象一定是线程安全的。但是不可变对象,不是所有的域是声明为final类型,它就是不可变的。

当满足以下条件是,对象才是不可变的:

  • 对象被创建以后,其状态就不能改变
  • 对象的所有域都是final类型。
  • 对象是被正确创建的。因为多线程中拿到的对象可能不是一个构建完整的对象。

总结

看完上面的内容,我们可能对并发编程有一个大概的了解。

  1. 并发编程的多线程安全问题和共享可变状态有关系
  2. Java中多线程的通信方式是通过共享内存来进行的。
  3. 无状态、不可变对象一定是线程安全的。
  4. 并发编程带来的原子性、可见性、重排序问题

参考:

《Java并发编程实战》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容