首先向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的更多相关文章

  1. java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock

    原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...

  2. 从源码学习Java并发的锁是怎么维护内部线程队列的

    从源码学习Java并发的锁是怎么维护内部线程队列的 在上一篇文章中,凯哥对同步组件基础框架- AbstractQueuedSynchronizer(AQS)做了大概的介绍.我们知道AQS能够通过内置的 ...

  3. Java并发编程锁系列之ReentrantLock对象总结

    Java并发编程锁系列之ReentrantLock对象总结 在Java并发编程中,根据不同维度来区分锁的话,锁可以分为十五种.ReentranckLock就是其中的多个分类. 本文主要内容:重入锁理解 ...

  4. java 并发多线程 锁的分类概念介绍 多线程下篇(二)

    接下来对锁的概念再次进行深入的介绍 之前反复的提到锁,通常的理解就是,锁---互斥---同步---阻塞 其实这是常用的独占锁(排它锁)的概念,也是一种简单粗暴的解决方案 抗战电影中,经常出现为了阻止日 ...

  5. Java并发 - (无锁)篇6

    , 摘录自葛一鸣与郭超的 [Java高并发程序设计]. 本文主要介绍了死锁的概念与一些相关的基础类, 摘录自葛一鸣与郭超的 [Java高并发程序设计]. 无锁是一种乐观的策略, 它假设对资源的访问是没 ...

  6. Java并发编程锁之独占公平锁与非公平锁比较

    Java并发编程锁之独占公平锁与非公平锁比较 公平锁和非公平锁理解: 在上一篇文章中,我们知道了非公平锁.其实Java中还存在着公平锁呢.公平二字怎么理解呢?和我们现实理解是一样的.大家去排队本着先来 ...

  7. Java并发编程-再谈 AbstractQueuedSynchronizer 1 :独占模式

    关于AbstractQueuedSynchronizer JDK1.5之后引入了并发包java.util.concurrent,大大提高了Java程序的并发性能.关于java.util.concurr ...

  8. 【Java并发】详解 AbstractQueuedSynchronizer

    前言 队列同步器 AbstractQueuedSynchronizer(以下简称 AQS),是用来构建锁或者其他同步组件的基础框架.它使用一个 int 成员变量来表示同步状态,通过 CAS 操作对同步 ...

  9. Java并发——显示锁

    Java提供一系列的显示锁类,均位于java.util.concurrent.locks包中. 锁的分类: 排他锁,共享锁 排他锁又被称为独占锁,即读写互斥.写写互斥.读读互斥. Java的ReadW ...

随机推荐

  1. BZOJ 1016--[JSOI2008]最小生成树计数(kruskal&搜索)

    1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 7429  Solved: 3098[Submit][St ...

  2. [JSOI2018]列队(主席树)

    跟上次那道列队不一样,但都是九条可怜...(吉老师太强了) 在主席树上统计答案,因为值域只有 \(10^6\) 甚至不用离散化... \(Code\ Below:\) #include <bit ...

  3. .NET Core 从1.1升级到2.0记录(Cookie中间件踩坑)

    .NET Core 2.0 新时代 万众瞩目的.NET Core 2.0终于发布了,原定于9.19的dotnetconf大会的发布时间大大提前了1个月,.NET Core 2.0/.NET Stand ...

  4. day 74 vue 2 axios数据请求 以及组件的学习

    前情提要:   vue 学习二: 一: 通过axios实现数据请求 1:json数据语法 json数据对象类似于JavaScript中的对象,但是它的键对应的值里面是没有函数方法的,值可以是普通变量, ...

  5. Python标准库 -- UUID模块(生成唯一标识)

    UUID是什么: UUID: 通用唯一标识符 ( Universally Unique Identifier ),对于所有的UUID它可以保证在空间和时间上的唯一性,也称为GUID,全称为: UUID ...

  6. Azure Storage架构介绍

    Windows Azure Storage由三个重要部分或者说三种存储数据服务组成,它们是:Windows Azure Blob.Windows Azure Table和Windows Azure Q ...

  7. Spring Boot 配置 IDEA&DevTools 自编译重启

    MAVEN 配置 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht ...

  8. PPT-常用快捷键

    Ctrl+Shift+C   复制文本格式 Ctrl+Shift+V   粘贴文本格式 Ctrl+B 应用粗体格式Ctrl+U 应用下划线Ctrl+l 应用斜体格式 全选  Ctrl + A 光标之后 ...

  9. OSRM笔记

    OSRM OSRM(OpenStreetMap Routeing Machine)可用于路线规划.作为高性能的路线规划引擎,OSRM使用C++14编写,基于开源的OpenStreetMap数据实现. ...

  10. Java中的构造器与垃圾回收

    构造器 在我们初始化对象时,如果希望设置一些默认值,那么就可以使用构造器,在Java中,构造器使用和类同名的名字且没有返回值,如下 class Test{ private String name; T ...