JAVA 并发:CLH 锁 与 AbstractQueuedSynchronizer
首先向Doug Lea致敬。
CLH
以下是CLH锁的一个简单实现:
class SimpleCLHLock {
/**
* initialized with a dummy node
*/
private Node dummy = new Node();
private AtomicReference<Node> tail = new AtomicReference<Node>(dummy);
/**
* implicit single linked list node
*/
private static class Node {
public volatile boolean locked = false;
}
private ThreadLocal<Node> threadLockNode = new ThreadLocal<Node>();
public void lock() {
Node last_tail;
Node new_tail = new Node();
new_tail.locked = true;
while (true) {
last_tail = tail.get();
if (tail.compareAndSet(last_tail, new_tail)) {
break;
}
}
// we just keep previous tail node as current node's direct predecessor
// and waiting it jump into unlocked state, so we form a single linked list implicitly
while (last_tail.locked) {
Thread.yield();
}
// we need the node reference when we want to unlock
// some threads may waiting on its state to be unlocked in above while-loop statement.
threadLockNode.set(new_tail);
}
public void unlock() {
// retrieve the lock state held by current thread
Node node = threadLockNode.get();
// if null then the current thread has not own the lock, also can't perform unlock operation
if (node == null) {
throw new IllegalMonitorStateException();
}
node.locked = false;
threadLockNode.set(null);
}
}
测试了不同的同步方式的速度
package lock;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by hegaofeng on 6/30/15.
*/
public class CLH {
private static volatile long count;
private static AtomicLong atomicLong = new AtomicLong();
private static ExecutorService pool = Executors.newCachedThreadPool();
public static void main(String[] args) {
final int deltaPerThreads = 10000;
final int numsOfThreads = 10;
Callable<Boolean> raw_counter = new Callable<Boolean>() {
@Override
public Boolean call() {
for (int i=0; i<deltaPerThreads; i++) count++;
return true;
}
};
Callable<Boolean> syn_counter = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
synchronized (CLH.class) {
count++;
}
}
return true;
}
};
Callable<Boolean> clh_counter = new Callable<Boolean>() {
private SimpleCLHLock lock = new SimpleCLHLock();
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
lock.lock();
count++;
lock.unlock();
}
return true;
}
};
final Callable<Boolean> lck_counter = new Callable<Boolean>() {
private ReentrantLock lock = new ReentrantLock();
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
lock.lock();
count++;
lock.unlock();
}
return true;
}
};
final Callable<Boolean> lck_fair_counter = new Callable<Boolean>() {
private ReentrantLock lock = new ReentrantLock(true);
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
lock.lock();
count++;
lock.unlock();
}
return true;
}
};
Callable<Boolean> sem_counter = new Callable<Boolean>() {
private Semaphore sem = new Semaphore(1);
@Override
public Boolean call() throws Exception {
for(int i=0; i<deltaPerThreads; i++) {
sem.acquire();
count++;
sem.release();
}
return true;
}
};
Callable<Boolean> sem_fair_counter = new Callable<Boolean>() {
private Semaphore sem = new Semaphore(1, true);
@Override
public Boolean call() throws Exception {
for(int i=0; i<deltaPerThreads; i++) {
sem.acquire();
count++;
sem.release();
}
return true;
}
};
Callable<Boolean> cas_counter = new Callable<Boolean>() {
private AtomicBoolean locked = new AtomicBoolean();
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
while (true) {
if (locked.compareAndSet(false, true)) {
count++;
locked.getAndSet(false);
break;
}
}
}
return true;
}
};
Callable<Boolean> pure_cas = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
atomicLong.incrementAndGet();
}
return true;
}
};
test("raw", raw_counter, deltaPerThreads, numsOfThreads);
test("syn", syn_counter, deltaPerThreads, numsOfThreads);
test("CLH", clh_counter, deltaPerThreads, numsOfThreads);
test("lock", lck_counter, deltaPerThreads, numsOfThreads);
test("lock_fair", lck_fair_counter, deltaPerThreads, numsOfThreads);
test("sem", sem_counter, deltaPerThreads, numsOfThreads);
test("sem_fair", sem_fair_counter, deltaPerThreads, numsOfThreads);
test("CAS", cas_counter, deltaPerThreads, numsOfThreads);
test("pure_CAS", pure_cas, deltaPerThreads, numsOfThreads);
}
public static void test(String name, Callable<Boolean> callable, final int deltaPerThreads, final int numsOfThreads) {
count = 0;
atomicLong.set(0);
pool = Executors.newCachedThreadPool();
List<Callable<Boolean>> calls = new ArrayList<Callable<Boolean>>();
for (int i=0; i<numsOfThreads; i++) {
calls.add(callable);
}
long start = System.nanoTime();
try {
pool.invokeAll(calls);
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.shutdown();
long end = System.nanoTime();
System.out.println("case : " + name);
System.out.println("counted: " + (name.equals("pure_CAS") ? atomicLong.get() : count));
System.out.println("target : " + numsOfThreads * deltaPerThreads);
System.out.println("time : " + (end - start) / 1e6 + "ms");
System.out.println();
}
}
结果:
case : raw
counted: 79270
target : 100000
time : 11.278ms
case : syn
counted: 100000
target : 100000
time : 13.553ms
case : CLH
counted: 100000
target : 100000
time : 1037.665ms
case : lock
counted: 100000
target : 100000
time : 29.134ms
case : lock_fair
counted: 100000
target : 100000
time : 638.944ms
case : sem
counted: 100000
target : 100000
time : 45.154ms
case : sem_fair
counted: 100000
target : 100000
time : 552.855ms
case : CAS
counted: 100000
target : 100000
time : 158.422ms
case : pure_CAS
counted: 100000
target : 100000
time : 31.964ms
- raw 表示不进行任何同步,现在没有保护的情况下计数累加产生了错误
- syn 表示使用内置锁同步方式即使用
synchronized块,在几种方式里表现最好 - CLH 表示使用自己实现的一个CLH锁(具有排队功能的自旋锁),这里的CLH的实现方式决定了它是一个公平锁
- lock 表示用ReentrantLock进行数据保护,速度仅次于内置锁,lock_fair是它的公平版本,不过速度上有大幅下降,变慢了将近20倍
- sem 表示使用Semaphore也就是信号量进行数据保护,速度也不错,sem_fair是它的公平版本,和ReentrantLock上出现的情况一样,公平版本比非公平版本出现了大幅的速度下降,慢了10倍
- CAS 用CAS操作实现简单的自旋锁,不具有排队功能
- pure_CAS 表示直接用使用AtomicLong类型的count变量进行计数,就不需要锁保护了,速度也是非常快得
调整测试的参数将线程数改为2,每线程增量改为50000即总增量不变的情况下得到的结果如下:
case : raw
counted: 91460
target : 100000
time : 4.767ms
case : syn
counted: 100000
target : 100000
time : 12.385ms
case : CLH
counted: 100000
target : 100000
time : 86.12ms
case : lock
counted: 100000
target : 100000
time : 33.91ms
case : lock_fair
counted: 100000
target : 100000
time : 108.119ms
case : sem
counted: 100000
target : 100000
time : 62.503ms
case : sem_fair
counted: 100000
target : 100000
time : 83.035ms
case : CAS
counted: 100000
target : 100000
time : 13.528ms
case : pure_CAS
counted: 100000
target : 100000
time : 11.377ms
基本锁
ReentrantLock的非公平版本在两次测试中并没有很大的变化,比较稳定。
公平锁
在不必要的情况下不去使用锁或者信号量的公平版本,它们相比非公平版本要慢很多,尤其当竞争非常激烈时。从第二组数据来看公平锁与非公平锁的差距缩小了很多,因为在竞争程度比较低(线程数少)的时候花在维护队列上的时间将大大减少。
CAS
正如Java并发编程中提到的CAS在高竞争的环境下性能不如直接使用锁,因为当竞争非常激烈时CAS花在重试上的时间将会大量增加(因为竞争激烈变量变化越快,CAS操作失败可能性越大)。而在竞争不是非常激烈的情况下CAS的开销更小,比如在第二次此时中的CAS自旋锁时间就比直接用ReentrantLock来得少。
内置锁
虽然说在Java并发编程一书中提到JDK1.6的Lock实现比synchronized要略快,这在JDK1.7中恐怕已经不成立了。
AbstractQueuedSynchronizer
http://javarticles.com/2012/10/abstractqueuedsynchronizer-aqs.html#prettyPhoto
JAVA 并发:CLH 锁 与 AbstractQueuedSynchronizer的更多相关文章
- java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock
原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...
- 从源码学习Java并发的锁是怎么维护内部线程队列的
从源码学习Java并发的锁是怎么维护内部线程队列的 在上一篇文章中,凯哥对同步组件基础框架- AbstractQueuedSynchronizer(AQS)做了大概的介绍.我们知道AQS能够通过内置的 ...
- Java并发编程锁系列之ReentrantLock对象总结
Java并发编程锁系列之ReentrantLock对象总结 在Java并发编程中,根据不同维度来区分锁的话,锁可以分为十五种.ReentranckLock就是其中的多个分类. 本文主要内容:重入锁理解 ...
- java 并发多线程 锁的分类概念介绍 多线程下篇(二)
接下来对锁的概念再次进行深入的介绍 之前反复的提到锁,通常的理解就是,锁---互斥---同步---阻塞 其实这是常用的独占锁(排它锁)的概念,也是一种简单粗暴的解决方案 抗战电影中,经常出现为了阻止日 ...
- Java并发 - (无锁)篇6
, 摘录自葛一鸣与郭超的 [Java高并发程序设计]. 本文主要介绍了死锁的概念与一些相关的基础类, 摘录自葛一鸣与郭超的 [Java高并发程序设计]. 无锁是一种乐观的策略, 它假设对资源的访问是没 ...
- Java并发编程锁之独占公平锁与非公平锁比较
Java并发编程锁之独占公平锁与非公平锁比较 公平锁和非公平锁理解: 在上一篇文章中,我们知道了非公平锁.其实Java中还存在着公平锁呢.公平二字怎么理解呢?和我们现实理解是一样的.大家去排队本着先来 ...
- Java并发编程-再谈 AbstractQueuedSynchronizer 1 :独占模式
关于AbstractQueuedSynchronizer JDK1.5之后引入了并发包java.util.concurrent,大大提高了Java程序的并发性能.关于java.util.concurr ...
- 【Java并发】详解 AbstractQueuedSynchronizer
前言 队列同步器 AbstractQueuedSynchronizer(以下简称 AQS),是用来构建锁或者其他同步组件的基础框架.它使用一个 int 成员变量来表示同步状态,通过 CAS 操作对同步 ...
- Java并发——显示锁
Java提供一系列的显示锁类,均位于java.util.concurrent.locks包中. 锁的分类: 排他锁,共享锁 排他锁又被称为独占锁,即读写互斥.写写互斥.读读互斥. Java的ReadW ...
随机推荐
- [ActionScript 3.0] as3处理xml的功能和遍历节点
as3比as2处理xml的功能增强了N倍,获取或遍历节点非常之方便,类似于json对像的处理方式. XML 的一个强大功能是它能够通过文本字符的线性字符串提供复杂的嵌套数据.将数据加载到 XML 对象 ...
- Swift5 语言指南(二十二) 扩展
扩展为现有的类,结构,枚举或协议类型添加新功能.这包括扩展您无法访问原始源代码的类型的能力(称为追溯建模).扩展类似于Objective-C中的类别.(与Objective-C类别不同,Swift扩展 ...
- cookie、session的区别
相信你肯定经常听说cookie和Session,那你有没有好好了解这两个的区别呢?其实,不整理之前,我也是一脸懵. 为什么需要cookie和session呢?---因为Http是无状态的,web开发中 ...
- 【sping揭秘】2、关于spring配置文件
<import>标签 引入其他的配置文件,如果A.xml 中的<bean>定义可能依赖B.xml中的某些<bean>定义,那么可以再A.xml中使用这种方式< ...
- [Umbraco] 熟悉管理页面
登录到umbraco管理界面后,发现其后台管理页很简洁 首页看到左侧部分是一个Content和Sections,右侧是管理区域 介绍各个Sections代表的含义: "Content&quo ...
- editplus来编写html
本来写这篇文章,我可以有很多废话,但是很多都过去了,言而总之下:我暂且给这个方法起个名字,叫做“为之法”,因为有了这篇文章,很多人的想法会豁然开朗,那样有了个名字交流传阅起来就方便多了. 本方法依托于 ...
- (转)MVC 与三层架构
原文:https://juejin.im/post/5929259b44d90400642194f3 MVC 与三层架构 一.简述 在软件开发中,MVC与三层架构这两个专业词汇经常耳闻,同时总有很多人 ...
- (转)SSL/TLS 漏洞“受戒礼”,RC4算法关闭
原文:https://blog.csdn.net/Nedved_L/article/details/81110603 SSL/TLS 漏洞“受戒礼” 一.漏洞分析事件起因2015年3月26日,国外数据 ...
- sizeof()与Marshal.SizeOf()的不同
在.NET中一般都是通过sizeof()或Marshal.SizeOf()来获取数据类型的大小,来简要地看一下它们二者有何不同. sizeof() sizeof()在MSDN中的介绍是,在编译时获得一 ...
- JVM(一)JVM的概述与运行流程
1.基本概念 JVM和普通虚拟机: 大家常用的两种虚拟机是VMWare和Visual Box,用来操作cpu指令. JVM是程序自己独立的运行环境,对堆栈.寄存器.字符码指令等操作. Java.JRu ...