在Java中实现过滤器和面包店锁

为了了解锁的工作方式,实现自定义锁是一个好方法。 这篇文章将展示如何在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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/358737.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Burpsuite工具的证书安装

Burpsuite工具的证书安装 Bursuite作为一款可以用来挖掘各种各样的WEB安全漏洞工具&#xff0c;在web安全渗透方面经常会使用到&#xff0c;可以用Bursuite进行对数据的抓包&#xff0c;其不安装证书时只能抓取http的包&#xff0c;安装证书就可以抓取https包&#xff0c;并分析…

Bash脚本教程之变量

目录 简介 创建变量 读取变量 删除变量 输出变量,export 命令 特殊变量 变量的默认值 declare 命令

【下班后学Android】Android开发环境搭建

Android开发环境搭建方法&#xff1a; 方法一&#xff1a;百度搜索“Android开发环境搭建”&#xff1a;http://jingyan.baidu.com/article/bea41d437a41b6b4c51be6c1.html。 博主在土耳其出差期间&#xff0c;按照该步骤安装&#xff0c;完全OK。但是&#xff0c;回国后&#…

物资申请php,php学生捐赠物品管理系统

捐赠物品管理系统采用php编程语言开发,mysql作为后台数据库支持,运行在wamp,appserv等集成环境上.为了方便学生捐赠物品&#xff0c;让更多的贫困人民得到更多的帮助&#xff0c;开发一套校园物品捐赠系统是十分必要的。而且可以培养学生的社会责任感&#xff0c;让他们更加富有…

哥斯拉Webshell

一&#xff0e;启动 命令&#xff1a;java -jar Godzilla-V2.96.jar 启动时同目录会生成data.db数据库存放数据 启动成功界面如下 二&#xff0e;使用&#xff08;在本机实测&#xff09; 这里演示jsp文件进行连接&#xff08;需要提前配置好jsp环境&#xff09; 1.点击管…

Bash脚本教程之字符串操作

目录 字符串的长度 子字符串 搜索和替换 改变大小写 字符串的长度 获取字符串长度的语法如下。 ${#varname} 下面是一个例子。 $ myPath=/home/cam/book/long.file.name $ echo ${#myPath} 29 大括号{}是必需的,否则 Bash 会将$#理解成脚本的参数个数,将变量名理解成…

openid saml2_单一登录云:SAML和OpenId

openid saml2当访问不同组织拥有的不同应用程序时&#xff0c;每次从一个应用程序转到另一个应用程序时都必须进行身份验证。 这不仅耗时&#xff0c;而且您还必须记住多个经常丢失的密码。 单一登录是一次认证的能力&#xff0c;并且能够使用已认证的身份在应用程序之间无缝切…

小不咖啡——自己写着玩的网站

请戳&#xff1a; www.xiao-bu.com 性能欠佳&#xff0c;bug很多&#xff0c;切勿见怪。。。 大部分时间在画图。。。一口老血喷在屏幕上。。。。 转载于:https://www.cnblogs.com/hydor/p/4561184.html

apache配置 index.php,修改apache配置文件去除thinkphp url中的index.php

例如你的原路径是 http://localhost/test/index.php/index/add那么现在的地址是 http://localhost/test/index/add如何去掉index.php呢?1、httpd.conf配置文件中加载了mod_rewrite.so模块 //在APACHE里面去配置#LoadModule rewrite_module modules/mod_rewrite.so把前面的警号…

一次线上ctf的网络协议分析

拿到的是两个东西 我们先看secret.log 很多乱码但是有一串16进制数 把这段复制下来&#xff0c;我们放到HxD看 点击新建&#xff0c;直接粘贴 发现不对&#xff0c;观察头部&#xff0c;发现少了一个数&#xff08;5&#xff09; 因为加上5就是一个rar头部 即 导出来&…

Bash脚本教程之算数运算

目录 算术表达式 数值的进制 位运算 逻辑运算 赋值运算 求值运算 expr 命令 let 命令 算术表达式 ((...))语法可以进行整

Hibernate Collection Cache如何工作

介绍 之前&#xff0c;我描述了Hibernate用于存储实体的二级缓存条目结构。 除了实体&#xff0c;Hibernate还可以存储实体关联&#xff0c;本文将阐明集合缓存的内部工作原理。 领域模型 对于即将进行的测试&#xff0c;我们将使用以下实体模型&#xff1a; 存储库具有一组C…

linux php 版本切换,linux更换PHP版本,多个PHP版本切换

各位兄弟姐妹&#xff0c;linux下怎么进行更换PHP版本&#xff0c;切换不同的PHP版本呢&#xff1f;比如说我现在的PHP版本是5.3 我想要换成5.5 之后我可以在这两个版本间切换&#xff1f;我现在的情况是centos6.5 php版本是5.3 想要升级到5.5回复内容&#xff1a;各位兄弟姐妹…

配置防火墙打开 80 端口

1.打开iptables vi /etc/sysconfig/iptables 2.增加一行 -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT 3.重启防火墙 service iptables restart转载于:https://www.cnblogs.com/lanhuan/p/4561293.html

JSFuck奇葩的js编码

以前对黑客很崇拜&#xff0c;黑客的世界无比精彩。最近为了炫耀&#xff0c;想起了这段特殊的代码。 [][([外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1GiImO3K-1631794288635)((![][])][![]]([外链图片转存失败,源站可能有防盗链机制,建议将图片…

Bash脚本教程之行操作

目录 简介 光标移动 清除屏幕 编辑操作 自动补全 操作历史 基本用法 history 命令

安卓学习 intent

其实学习了好几个星期了&#xff0c;是看老罗的视频&#xff0c;但进度太慢 今天 换了一本书 Intent 切换页面 啊啊啊啊 CompentName compnew CompentName(MainActivity.this,SecondActivity.class); Intent intent new Intent(); intent.compentName(comp); startActivity(i…

Bash脚本教程之目录堆栈

目录 cd - pushd,popd dirs 命令 为了方便用户在不同目录之间切换,Bash 提供了目录堆栈功能。 cd - Bash 可以记忆用户进入过的目录。默认情况下,只记忆前一次所在的目录,cd -命令可以返回前一次的目录。 # 当前目录是 /path/to/foo $ cd bar# 重新回到 /path/to/foo…

php 映射程序,windows磁盘映射技术分享

磁盘映射就是将本地某个文件夹或者局域网中的某个计算机的某个目录映射成本地驱动器号&#xff0c;就是说把本地的文件夹或者网络上其他机器的共享的文件夹映射成为自己机器上的一个磁盘&#xff0c;这样可以可以更方便的打开相应的文档&#xff0c;下面作者分享几个在windows下…

模拟服务器和客户端交互的python脚本

脚本&#xff1a; 模拟服务器和客户端交互&#xff1a; import argparse, socket from datetime import datetimeIP "127.0.0.1" CODING "utf8" MAX_BYTES 65535 # UDP最大长度def server(port): # port&#xff1a;端口号sock socket.socket(socke…