Java 并发/多线程教程(八)-竞态条件和临界区

      本系列译自jakob jenkov的Java并发多线程教程,个人觉得很有收获。由于个人水平有限,不对之处还望矫正!

      竞态条件是在临界区内可能发生的一种特殊情况。临界区是多线程并发执行一代码,根据线程的执行顺序可能产生多种结果的区域。多线程在临界区执行代码的结果可能不一样,不同的结果取决于线程的执行顺序。也就是说,临界区包含竞态条件。竞态一词源于隐喻,线程在临界区进进行资源竞争,在临界区的资源竞争影响最后的结果。

     这听起来有点复杂,在下面的篇幅中,将会在下面的篇幅中介绍更多与竞态条件和临界区的相关内空。

临界区

     在一个应用的内部执行多个线程本身不会导致什么问题,当多个线程同时访问相同的资源,问题就出现了。举个例子:相同的内存资源(变量、数组、或者对象),系统资源(数据库、webservice)或者文件。

      实事上,这种问题仅出现在多个线程同时对这些资源进行写操作时。只要资源不更改,多个线程同时对相同的资源进行读是安全的。

      下面的这个例子当多个线程同时执行时可能会出错:

public class Counter{

    protected long count= 0;    

    public void add(long value){

         this.count=this.count+value;

   }

}

假设有两个线程A和B,在Counter的同一个实例上同时执行add方法,我们没办法预知操作系统在这两个线之间的调度顺序。这段代码在JVM中不是以原子操作的方式执行。而是把他们当作一组指令去执行:

1、从内存中读取this.count的值到寄存器

2、把值添添加后写到寄存器

3、把寄存器中的值写回到内存中

观察线程A和线程B的执行,将会发生些什么。

this.count = 0;

A: Reads this.count into register(0);

B:Reads this.count into register(0);

B:Add value 2 to register;

B:Writes register value(2) back to memory, this.count now equals 2

A:Add value 3 to register;

A:Writes register value(3) back to memory,this.count now equals 3

      这两个线程的目的是想对count进行加2,加3操作。因此这两个线程的执行结果预期应该为5,然而,由于这两个线程的交错执行,结果将会不同。 在上面的例子中,A线程和B线程刚开始从内存中读取到的数据都是0,然后它们各自将值加到counter上,然后将值写回到内存中,而不是5,this.count的值的最后的值就是最后一个写这个值的线程,在上面的例子中,他可能是A线程,也有可能会是B线程。

临界区的竞态条件

       在上面的例子中,当执行add()方法时,当多线程去执行这段代码时,在临界区就产生了竞态条件。

      当两个线程访问相同的资源,他们的访问顺序就叫做竞态条件,导致竞态条件产生的代码区就是临界区。

预防竞态条件

        要预防竞态条件的产生,你要确保临界区的代码以原子指令执行。也就是说一次只有单一的线程去执行它,在这个线程执行完,离开临界区之前,没有其他的线程可以执行。

      竞态条件也可以通过临界区的线程同步来避免。线程同步可以采用java同步代码块、锁或者原子变量(java.util.concurrent.atomic.AtomicInteger)来实现 。

临界区吞吐量

对于较小的临界区,使得整个临界区一起同步工作。但是,较大的临界区分解成较小的临界区有可能会有好处,它允许多个线程在较小的临界区中执行,这样可以减少共享资源的竞争,提升整个临界区的吞吐量。

下面由简单的例子来说明,我想要表达的意思:

public class TwoSums{

    private int sum1 = 0;

    private int sum2 = 0;

    public void add(int val1,int val2){

         synchronized(this){

               this.sum1 +=val1;

               this.sum2 +=val2;

          }

     }

}

注意,这个add()方法把相加后的值赋给两个不同的变量。为了避免竞态条件,求和的代码在java的同步代码块中执行。通过这种方式,在同时执行这段代码时,只有一个线程执行求和操作。然而,由于两个求和参数是两个完全独立的变量,你可以把他们分散到两个同步代码块中去求和。就像下面一样:

public class TwoSums{

   private int sum1 = 0;

   private int sum2 = 0;

   private Integer sum1Lock  = new Integer(1);

   private Integer sum2Lock = new Integer(2);

   private void add(int val1,int val2){

         synchronized(this.sum1Lock){

               this.sum1 += val1;

         }

         synchronized(this.sum2Lock){

               this.sum2 += val2;

          }

   }

}

现在两个线程可以同时执行add()方法。其中一个线程进行第一个同步代码块,第二线程进入到第二个同步代码块,由于这两个同步代码对不同的对象进行同步操作,所以两个不同的线程可以各自独立的去执行这两个代码块。通过这种方式,线程可以尽量少的减少等待去执行add()方法。

这个例子非常简单,当然,在实际的运用中,共享资源可能分解可能更为复杂,需要更多的去分析他们执行顺序的可能性。

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

推荐阅读更多精彩内容