为了了解锁的工作方式,实现自定义锁是一个好方法。 这篇文章将展示如何在Java上实现Filter和Bakery锁(自旋锁),并将它们的性能与Java的ReentrantLock进行比较。 过滤器锁和面包房锁满足互斥并且也是无饥饿算法,面包房锁是先到先服务的锁[1]。

为了进行性能测试,使用不同的锁类型,不同的线程数和不同的次数将计数器值递增到10000000。 测试系统配置为:Intel Core I7(具有8个核心,其中4个是真实的),Ubuntu 14.04 LTS和Java 1.7.0_60。

过滤器锁具有n-1个级别,可以视为“候诊室”。 获取锁之前,必须有一个线程穿过此等候室。 级别[2]有两个重要属性:

  1. 至少一个尝试进入级别l的线程成功。
  2. 如果有多个线程试图进入级别l ,则至少一个线程被阻止(即继续在该级别上等待)。

过滤器锁定的实现如下:

/**
* @author Furkan KAMACI
*/
public class Filter extends AbstractDummyLock implements Lock {
/* Due to Java Memory Model, int[] not used for level and victim variables.
Java programming language does not guarantee linearizability, or even sequential consistency,
when reading or writing fields of shared objects
[The Art of Multiprocessor Programming. Maurice Herlihy, Nir Shavit, 2008, pp.61.]
*/
private AtomicInteger[] level;
private AtomicInteger[] victim;
private int n;
/**
* Constructor for Filter lock
*
* @param n thread count
*/
public Filter(int n) {
this.n = n;
level = new AtomicInteger[n];
victim = new AtomicInteger[n];
for (int i = 0; i < n; i++) {
level[i] = new AtomicInteger();
victim[i] = new AtomicInteger();
}
}
/**
* Acquires the lock.
*/
@Override
public void lock() {
int me = ConcurrencyUtils.getCurrentThreadId();
for (int i = 1; i < n; i++) {
level[me].set(i);
victim[i].set(me);
for (int k = 0; k < n; k++) {
while ((k != me) && (level[k].get() >= i && victim[i].get() == me)) {
//spin wait
}
}
}
}
/**
* Releases the lock.
*/
@Override
public void unlock() {
int me = ConcurrencyUtils.getCurrentThreadId();
level[me].set(0);
}
}

面包店锁定算法通过使用面包店中常见的数字分配机的分布式版本来维护先到先得的属性:每个线程在门口取一个数字,然后等待,直到没有尝试使用更早编号的线程为止输入[3]。

面包店锁的实现如下:

/**
* @author Furkan KAMACI
*/
public class Bakery extends AbstractDummyLock implements Lock {
/* Due to Java Memory Model, int[] not used for level and victim variables.
Java programming language does not guarantee linearizability, or even sequential consistency,
when reading or writing fields of shared objects
[The Art of Multiprocessor Programming. Maurice Herlihy, Nir Shavit, 2008, pp.61.]
*/
private AtomicBoolean[] flag;
private AtomicInteger[] label;
private int n;
/**
* Constructor for Bakery lock
*
* @param n thread count
*/
public Bakery(int n) {
this.n = n;
flag = new AtomicBoolean[n];
label = new AtomicInteger[n];
for (int i = 0; i < n; i++) {
flag[i] = new AtomicBoolean();
label[i] = new AtomicInteger();
}
}
/**
* Acquires the lock.
*/
@Override
public void lock() {
int i = ConcurrencyUtils.getCurrentThreadId();
flag[i].set(true);
label[i].set(findMaximumElement(label) + 1);
for (int k = 0; k < n; k++) {
while ((k != i) && flag[k].get() && ((label[k].get() < label[i].get()) || ((label[k].get() == label[i].get()) && k < i))) {
//spin wait
}
}
}
/**
* Releases the lock.
*/
@Override
public void unlock() {
flag[ConcurrencyUtils.getCurrentThreadId()].set(false);
}
/**
* Finds maximum element within and {@link java.util.concurrent.atomic.AtomicInteger} array
*
* @param elementArray element array
* @return maximum element
*/
private int findMaximumElement(AtomicInteger[] elementArray) {
int maxValue = Integer.MIN_VALUE;
for (AtomicInteger element : elementArray) {
if (element.get() > maxValue) {
maxValue = element.get();
}
}
return maxValue;
}
}

对于此类算法,应提供或使用从0或1开始并以一个增量递增的线程id系统。 线程的名称为此目的进行了适当设置。 还应该考虑:Java编程语言在读取或写入共享对象的字段时不能保证线性化甚至顺序一致性[4]。 因此,过滤器锁的级别和受害变量,面包店锁的标志和标签变量定义为原子变量。 一方面,想要测试Java内存模型效果的人可以将该变量更改为int []和boolean [],并使用两个以上的线程运行算法。 然后,可以看到即使线程处于活动状态,该算法也将针对Filter或Bakery挂起。

为了测试算法性能,实现了一个自定义计数器类,该类具有getAndIncrement方法,如下所示:

/**
* gets and increments value up to a maximum number
*
* @return value before increment if it didn't exceed a defined maximum number. Otherwise returns maximum number.
*/
public long getAndIncrement() {
long temp;
lock.lock();
try {
if (value >= maxNumber) {
return value;
}
temp = value;
value = temp + 1;
} finally {
lock.unlock();
}
return temp;
}

公平测试多个应用程序配置存在最大的障碍。 考虑的是:有很多工作(将变量递增到所需的数量),并且在线程数量不同的情况下,完成它的速度有多快。 因此,为了进行比较,应该有一个“工作”平等。 此方法还使用该代码段测试不必要的工作负载:

if (value >= maxNumber) {
return value;
}

比较多个线程时,一种计算线程的单位工作性能的方法(即,不设置最大障碍,在循环中迭代到最大数量,然后将最后一个值除以线程数量)。

此配置用于性能比较:

线程数 1,2,3,4,5,6,7,8
重试计数 20
最大人数 10000000

这是包含标准误差的结果图表:

首先,当您在Java中多次运行代码块时,会对代码进行内部优化。 当算法多次运行并将第一输出与第二输出进行比较时,可以看到此优化的效果。 因此,第一次经过的时间通常应大于第二行。 例如:

currentTry = 0, threadCount = 1, maxNumber = 10000000, lockType = FILTER, elapsedTime = 500 (ms)
currentTry = 1, threadCount = 1, maxNumber = 10000000, lockType = FILTER, elapsedTime = 433 (ms)

结论

从图表中可以看出,面包房锁比过滤器锁快,标准误差低。 原因是筛选器锁定的锁定方法。 在Bakery Lock中,作为一种公平的方法,线程是一个一个地运行的,但是在Filter Lock中,它们是相互计算的。 与其他Java相比,Java的ReentrantLock具有最佳的性能。

另一方面,Filter Lock线性地变差,但是Bakery和ReentrantLock却不是(当线程运行更多的线程时,Filter Lock可能具有线性图形)。 更多的线程数并不意味着更少的经过时间。 由于创建和锁定/解锁线程,因此2个线程可能比1个线程差。 当线程数开始增加时,Bakery和ReentrantLock的经过时间会变得更好。 但是,当线程数持续增加时,它就会变得更糟。 原因是运行算法的测试计算机的真实核心编号。

  • 可以从此处下载用于在Java中实现过滤器和面包店锁的源代码: https : //github.com/kamaci/filbak
  1. 多处理器编程的艺术。 莫里斯·赫里希(Maurice Herlihy),《尼尔·沙维特》(Nir Shavit),2008年,第31.-33页。
  2. 多处理器编程的艺术。 莫里斯·赫里希(Maurice Herlihy),《尼尔·沙维特》(Nir Shavit),2008年,第28页。
  3. 多处理器编程的艺术。 莫里斯·赫利希(Maurice Herlihy),《尼尔·沙维特》(Nir Shavit),2008年,第31页。
  4. 多处理器编程的艺术。 莫里斯·赫利希(Maurice Herlihy),《尼尔·沙维特》(Nir Shavit),2008年,第61页。

翻译自: https://www.javacodegeeks.com/2015/05/implementing-filter-and-bakery-locks-in-java.html

在Java中实现过滤器和面包店锁相关推荐

  1. java面包屑实现_在Java中实现过滤器和面包店锁

    java面包屑实现 为了了解锁的工作原理,实现自定义锁是一种好方法. 这篇文章将展示如何在Java上实现Filter和Bakery锁(自旋锁),并将它们的性能与Java的ReentrantLock进行 ...

  2. java中的过滤器与监听器

    过滤器: Filter本身并不生成请求和响应对象,只是提供过滤功能. Filter能够在Servlet被调用之前检查Request对象,并修改Request Header和Request内容:在Ser ...

  3. java中ReentrantLock实现,公平锁和非公平锁,AQS并发队列,

    一般在java中,遇到并发的时候,我们很多时候可能会使用synchronized关键字来实现锁,但是synchronized关键字有一定的缺陷(比如无法实现类似读锁.非公平),而Lock可以实现.在j ...

  4. Java中的共享锁和排他锁(以读写锁ReentrantReadWriteLock为例)

    重要声明:本人之前对java中的读写锁也不是非常了解,用的也不是很多,尤其在读写锁的策略原理一块没有深究过,本篇文章是在学习[玩转Java并发工具,精通JUC,成为并发多面手]课程后写的,故文章类型选 ...

  5. java中使用Redis实现分布式锁

    前言 目前很多大型的互联网公司后端都采用了分布式架构来支撑前端应用,其中服务拆分就是分布式的一种体现,既然服务拆分了,那么多个服务协调工作就会出现一些资源竞争的情况.比如多个服务对同一个表中的数据进行 ...

  6. Java中的过滤器和拦截器

    一.简介 1.什么是拦截器? (1)在AOP中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作.拦截是AOP的一种实现策略. (2)拦截器是动态拦截Action调用的对象.它提供了 ...

  7. java中的CAS操作以及锁机制详解

    关于CAS操作 CAS:Compare And Swap   --  乐观锁策略 CAS(无锁操作):使用CAS叫做比较交换来判断是否出现冲突,出现冲突就重试当前操作直到不冲突为止. 悲观锁(JDK1 ...

  8. java过滤器如何操作数据库_jsp – 使用Java中的过滤器验证用户名,密码(与数据库联系)...

    String sql="select * from reg where username='"+user+"' and pass='"+pwd+"'& ...

  9. java filter 跳过_如何在java中的过滤器链中跳过一个过滤器

    您可以在请求中设置属性,并在第二个过滤器中进行检查. public class FirstFilter implements Filter { //... public void doFilter(S ...

最新文章

  1. 剑指offer: 二进制中1的个数 python 实现
  2. linux 硬链接和软链接
  3. Junit内部解密之四: Junit单元测试最佳实践
  4. caffe学习笔记18-image1000test200数据集分类与检索完整过程
  5. 我的Java自定义线程池执行器
  6. 马斯克发推:8月特斯拉Autopilot实现完全自动驾驶
  7. 脑电波技术新突破:读心准确率达到95%
  8. 基于二阶锥规划的主动配电网动态最优潮流求解
  9. Gocator三维传感器环境配置结合VS2015 (Gocator自带网页软件使用介绍)
  10. MEMS激光雷达监测法兰克福机场客流量应用案例
  11. 补丁服务器同步不上微软,无法初始化 Microsoft Exchange 信息存储服务,因为客户端和服务器计算机上的时钟不同步...
  12. python 函数修饰器 父类_Python函数嵌套、回调函数、闭包、装饰器和类
  13. 陈启峰 Size Balanced Tree
  14. lehigh计算机就业,2016美国大学计算机专业排名
  15. 量子退火Python实战(2):护士调度问题(NSP : Nurse Scheduling Problem)
  16. 【NVCaffe源码分析】数据增量之batch_sampler
  17. ETHEOS开发资源及工具集合
  18. js距离米转换为千米_公里和千米的换算(千米和米换算公式)
  19. 如何找到时序列中的缺失月(missing month)/天(missing day)并补全?尤其针对多个客户或产品的月/天销量 -- 史上最简单方法,只要2行python代码
  20. K8S部署skywalking

热门文章

  1. 《四世同堂》金句摘抄(二)
  2. idea左右切换页面 返回上一次鼠标位置
  3. 【spring boot】启动类启动 错误: 找不到或无法加载主类 com.codingapi.tm.TxManagerApplication 的解决方案
  4. 19年8月 字母哥 第一章 spring boot 2.x基础及概念入门 这里全部看完了 热部署没出来 第二章在前面2页 用热点公司网不行
  5. layui 单独使用日期组件
  6. python 高维数据_Python数据分析入门|利用NumPy高效处理高维数据
  7. 机器人点焊枪接线_用于焊接机器人焊枪工具点及工件坐标系标定装置及方法与流程...
  8. 转: 理解RESTful架构
  9. nginx负载均衡与反向代理
  10. ReviewForJob(2)算法分析