Java高并发12-避免伪共享和锁机制

1、复习

2、如何避免伪共享

  • 在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参数git

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

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

4、乐观锁和悲观锁

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字段或者使用业务状态来实现,乐观锁直到提交时才锁定,因此不会产生任何死锁。

5、公平锁和非公平锁

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

六 、源码: