zoukankan      html  css  js  c++  java
  • Java高并发12-避免伪共享和锁机制

    一、复习

    二、如何避免伪共享

    • 在JDK8之前是使用填充字节的方式来避免伪共享的,我们最终的目的其实就是希望单个变量能够独占一个缓存行。
    • 我们举一个类的例子
    package com.ruigege.OtherFoundationOfConcurrent2;

    public class FiledLong {
     public volatile long value =0L;
     public long p1,p2,p3,p4,p5,p6;
    }

    • 可以看这个类,如果cache行伪64个字节,那么正好能够占满,七个long类型的变量,其中p1-p6都是用来占位,还有一个对象的头占用八个字节,正好64个字节。
    • 在JDK8中提供了一个注解,用于避免伪共享
    @sun.misc.Contended
    class FiledLong2{
     public volatile long value=0L;
    }
    • 用法:既可以用修饰类也可以用来修饰变量。

    注意点:@Contended注解只能用于Java的核心类,比如rt包下的类,如果用户的类需要使用这个注解的时候,需要添加JVM的参数:-XX:-RestrictContended,填充的默认宽度为128,要自定义宽度可以设置-XX:ContendedPaddingWidth参数

    三、出现伪共享内存的条件

    • 在多线程下访问同一个缓存行的多个变量,才会出现伪共享变量的问题,如果在单线程下访问多个变量反而会加速访问。

    四、乐观锁和悲观锁

    1.悲观锁

    • 定义:悲观锁认为外界对数据的修改持保守态度,认为数据很容易被外界修改,在数据被处理之前必须先加锁,这种锁是排他锁,一个线程获取了锁之后,其他线程只能等待或者抛出异常。获取锁的线程,对记录进行操作,然后提交事务释放锁。下面举一个例子
     //使用悲观锁来获取
     EntryObject entry = query("select * from table1 where id =#{id} for update",id);
     //修改记录内容,根据计算修改entry记录的属性
     String name=generatorName(entry);
     entry.setName(name);
     
     //update操作
     int count = updateZ("update table1 set name=#{name},age=#{age} where id=#{id}",entry);
     return count;
    • 对于上面的代码,使用了事务切面的方法,只要进入这个方法种就开始执行事物一直到这个方法结束,多个线程调用这个方法的时候,只有一个线程能够获取锁,其他线程就会阻塞挂起,直到原线程释放锁
    • 乐观锁是相对悲观锁而存在的的方式,一般认为如果只是访问数据那么就是可以不用加锁,只有要更新数据的时候,才会正式对数据冲突与否进行检测,具体来说,根据update返回的行数让用户决定如何去做。将上面的例子改为乐观锁。
    package com.ruigege.OtherFoundationOfConcurrent2;

    public class UpdateEntry2 {

     public int updateEntry(long id) {
      //使用乐观锁获取指定记录
      EntryObject entry = query("select * from table1 where id=#{id}",id);
      
      //
      String name = generatorName(entry);
      entry.setName(name);
      
      //update操作
      int count = update("update table1 set name=#{name},age=#{age},version=${version}+1 where id=#{id} and version =#{version}",entry);
      return count;
     }
    }
    • 对比上面的代码就可以知道根据version来进行更新数据,更新成功的话,就会给version+1,其他线程进行更新的时候,如果vesion不对的话,那么就会停止更新。我们也是使用不断地循环来获取锁而解决更新的问题
    package com.ruigege.OtherFoundationOfConcurrent2;

    public class updateEntry3 {

     boolean result = false;
     int retryNum = 5;
     while(retryNum>0) {
      //使用乐观锁获取记录
      EntryObject entry = query("select * from table1 where id=#{id}",id);
      String name = generatorName(entry);
      entry.setName(name);
      
      //update操作
      int count = update("update table1 set name=#{name},age=#{age},version=${version}+1 where id=#{id} and version =#{version}",entry);
      //返回的行如果不是0的话说明更新成功了,那么即刻跳出循环
      if(count == 1) {
       result = true;
       break;
      }
      retryNum--;
      
     }
     return result;
    }
    • 乐观锁并不会使用数据库提供的锁机制,一般在表中添加version字段或者使用业务状态来实现,乐观锁直到提交时才锁定,所以不会产生任何死锁。

    五、公平锁和非公平锁

    • 公平锁表示线程获取锁的顺序是按照先到先得的原则,非公平锁在运行的时候闯入,也就是不一定先到先得。
    • ReentrantLock提供了公平和非公平锁。

    六 、源码:

  • 相关阅读:
    shell脚本按当前日期输出日志
    bat弹出确认或取消窗口
    bat脚本输出日志
    北京浩赢科技有限公司与好宝多多
    bat延迟执行脚本,利用choice实现定时执行功能
    centos下安装tunctl
    OpenStack Train版 简单部署流程(4)- octavia
    LVM基础
    OpenStack Train版 简单部署流程(3)- ceilometer
    openstack stein
  • 原文地址:https://www.cnblogs.com/ruigege0000/p/14077330.html
Copyright © 2011-2022 走看看