Java_并发/多线程教程-竞态条件和临界区

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

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

临界区

在一个应用内部执行多个线程本身不会导致什么问题,当多个线程同事访问相同的资源,问题就出现了。举个例子:相同的内存资源(变量、数组、或者对象),系统资源(数据库、webservice)或者文件。
事实上,这种问题仅出现在多个线程同时对这些资源进行写操作时。只要资源不改,多个线程同时对相同的资源进行读是安全的。

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

    public class Counter{
     protected long count =0; 
     public void add(long value){
        this.count=this.count+value;
        
     }
     
        }

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

1.从内存中读取this.count的值到寄存器
2.把值添加后写到寄存器
3.把寄存器的值写回到内存中

观察线程A和线程B的执行,将会发生写什么。
this.count = 0;
A: Reads this.count into register(0);
B:Reads this.coun1t 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)
      {
        this.sum1+=val1;
        
      }
      synchronized(this.sum2Lock)
      {
         this.sum2+=val2;
         
      }
  }

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

转载文章

https://developer.aliyun.com/article/247807#:~:text=%E7%AB%9E%E6%80%81%E6%9D%A1%E4%BB%B6%E6%98%AF%E5%9C%A8%E4%B8%B4%E7%95%8C%E5%8C%BA%E5%86%85%E5%8F%AF%E8%83%BD%E5%8F%91%E7%94%9F%E7%9A%84%E4%B8%80%E7%A7%8D%E7%89%B9%E6%AE%8A%E6%83%85%E5%86%B5%E3%80%82%20%E4%B8%B4%E7%95%8C%E5%8C%BA%E6%98%AF%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%B9%B6%E5%8F%91%E6%89%A7%E8%A1%8C%E4%B8%80%E4%BB%A3%E7%A0%81%EF%BC%8C%E6%A0%B9%E6%8D%AE%E7%BA%BF%E7%A8%8B%E7%9A%84%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F%E5%8F%AF%E8%83%BD%E4%BA%A7%E7%94%9F%E5%A4%9A%E7%A7%8D%E7%BB%93%E6%9E%9C%E7%9A%84%E5%8C%BA%E5%9F%9F%E3%80%82,%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%9C%A8%E4%B8%B4%E7%95%8C%E5%8C%BA%E6%89%A7%E8%A1%8C%E4%BB%A3%E7%A0%81%E7%9A%84%E7%BB%93%E6%9E%9C%E5%8F%AF%E8%83%BD%E4%B8%8D%E4%B8%80%E6%A0%B7%EF%BC%8C%E4%B8%8D%E5%90%8C%E7%9A%84%E7%BB%93%E6%9E%9C%E5%8F%96%E5%86%B3%E4%BA%8E%E7%BA%BF%E7%A8%8B%E7%9A%84%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F%E3%80%82%20%E4%B9%9F%E5%B0%B1%E6%98%AF%E8%AF%B4%EF%BC%8C%E4%B8%B4%E7%95%8C%E5%8C%BA%E5%8C%85%E5%90%AB%E7%AB%9E%E6%80%81%E6%9D%A1%E4%BB%B6%E3%80%82%20%E7%AB%9E%E6%80%81%E4%B8%80%E8%AF%8D%E6%BA%90%E4%BA%8E%E9%9A%90%E5%96%BB%EF%BC%8C%E7%BA%BF%E7%A8%8B%E5%9C%A8%E4%B8%B4%E7%95%8C%E5%8C%BA%E8%BF%9B%E8%BF%9B%E8%A1%8C%E8%B5%84%E6%BA%90%E7%AB%9E%E4%BA%89%EF%BC%8C%E5%9C%A8%E4%B8%B4%E7%95%8C%E5%8C%BA%E7%9A%84%E8%B5%84%E6%BA%90%E7%AB%9E%E4%BA%89%E5%BD%B1%E5%93%8D%E6%9C%80%E5%90%8E%E7%9A%84%E7%BB%93%E6%9E%9C%E3%80%82

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值