资源竞速(Race Conditions)和临界区(Critical Sections)临界区临界区的资源竞速避免资源竞速临界区的吞吐量

  • 临界区
  • 临界区的资源竞速
  • 避免资源竞速
  • 临界区的吞吐量

critical section是每个线程中访问临界资源的那段代码,不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。 资源竞速就是可能在由于在访问临界区时没有互斥的访问而导致的特殊情况。

如果多个线程在临界区的执行结果可能因为代码的执行顺序不同而出现不同的结果,我们就说这时候在临界区出现了资源竞速的情况。

我们接下来会详细的介绍资源竞速和临界区的概念

临界区

当多个线程访问相同资源的时候,就会出现问题。比如说访问相同的内存区域(变量,数组或者对象),系统资源(数据库,文件等) 而且,一般来说,只在多个线程对这个资源进行写操作的时候才会出现问题,如果是简单的读操作,不改变资源的话,显然是不会出现问题的。 Here is a critical section Java code example that may fail if executed by multiple threads simultaneously: 下面这段临界区的代码,如果被多个线程执行就可能出现问题:

 public class Counter {

     protected long count = 0;

     public void add(long value){
         this.count = this.count + value;
     }
  }

我们假设有两个线程A和B同时执行add方法在相同的实例上,我们无法获知什么时候操作系统进行线程的切换,而且代码中的add操作对jvm来说不是原子操作,而是由一下类似的几步指令:

  • load,从内存中将this.count的值load到寄存器上
  • 在寄存器上进行加法运算
  • 将寄存器的值写回到内存中

我们看看如果按下面的执行顺序,结果会是什么样?

   A:  Reads this.count into a register (0)
   B:  Reads this.count into a register (0)
   B:  Adds value 2 to register
   B:  Writes register value (2) back to memory. this.count now equals 2
   A:  Adds value 3 to register
   A:  Writes register value (3) back to memory. this.count now equals 3

我们如果按照上述的执行顺序,counter最后结果会是3,但我们都知道最后的值应该为5才对,但由于上述指令是交叉执行的,所以最后会导致不一样的结果。

临界区的资源竞速

add方法中包括了一个临界区,当多个线程访问临界区时,就会出现资源竞速的问题。 更标准的说,当两个或多个线程竞争同一个资源时,对资源的访问顺序就变的很关键了,这就是资源竞速的问题,一段代码块导致资源竞速的问题就是临界区代码。

避免资源竞速

为了避免资源竞速的问题,我们必须保证临界区的代码必须原子性。这就意味着当一个线程进入临界区执行时,其他线程不能进入这个线程执行,除非那个线程离开了临界区,也就是说只有一个线程能在临界区执行在某个时刻。

为了避免资源竞速的问题,我们可以使用synchronized关键字同步代码块,或者使用lock对象,或者使用atomic variables。我们将在后续文章中讲述。

临界区的吞吐量

对于小的临界区,我们直接将整个代码块标为synchronized就可以避免资源竞速了。但是对于较大的临界区,我们为了执行效率,最好将代码分为小的临界区,并分别同步的不同的临界区,因为我们知道synchronized的关键字的影响是比较大的。如果我们直接同步整个临界区,很可能会影响临界区的吞吐量。 我们通过下面这个例子来具体讲述:

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;
        }
    }
}

为了避免资源竞速,我们将两个加法操作同步了,这样的话,在某一个时刻,只有一个时刻可以执行加法操作。

然而,因为两个sum变量都是互相独立的,所以我们可以将两个变量分别分成不同的同步块中:

public class TwoSums {
    
    private int sum1 = 0;
    private int sum2 = 0;

    private Integer sum1Lock = new Integer(1);
    private Integer sum2Lock = new Integer(2);

    public void add(int val1, int val2){
        synchronized(this.sum1Lock){
            this.sum1 += val1;   
        }
        synchronized(this.sum2Lock){
            this.sum2 += val2;
        }
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏乐百川的学习频道

JUnit 5 简介

著名的Java单元测试框架Junit 4已经出来很长时间了,当时我发现JUnit 5已经处于测试版,就准备写文章来介绍JUnit 5.不过因为还是测试版,所以有...

25990
来自专栏Java3y

SpringMVC入门就这么简单

什么是SpringMVC? SpringMVC是Spring家族的一员,Spring是将现在开发中流行的组件进行组合而成的一个框架!它用在基于MVC的表现层开发...

66460
来自专栏Hongten

HIbernate的“1+N”问题

import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.T...

11530
来自专栏阿杜的世界

Java Web技术经验总结(八)

使用XML文件中的mvc:annoation-driven元素也可以,具体代码如下:

11030
来自专栏ImportSource

必须知道的Spring Boot中的一些Controller注解

本文旨在向你介绍在Spring Boot中controller中最基本的一些注解,不可能涵盖所有的,但至少让你了解最基本的,然后可以通过这些注解来写出一个API...

4.1K100
来自专栏IT 指南者专栏

Spring框架系列(二)之Bean的注解管理

微信公众号:compassblog 欢迎关注、转发,互相学习,共同进步! 有任何问题,请后台留言联系! 1、Spring中的两种容器 在系列(一)中我们已经知道...

35360
来自专栏JavaEdge

SpringMVC之@CookieValue注解使用@CookieValue注解映射cookie值

34860
来自专栏云霄雨霁

Java--Spring框架基础

16240
来自专栏青青天空树

spring基础(2:最小化XML配置)

  byType在出现多个匹配项时不会自动选择一个然是报错,为避免报错,有两种办法:1.使用<bean>元素的primary属性,设置为首选Bean,但所有be...

11420
来自专栏老付的网络博客

SpringMVC 教程

   Spring 是目前比较流行的MVC框架,让POJO处理起来变的容易,也支持Rest的Url请求。采用松散的耦合可插拔的接口,比其它MVC接口更具有扩展...

20540

扫码关注云+社区

领取腾讯云代金券