锁定老帖子 主题:最简单高效的tryLock
精华帖 (0) :: 良好帖 (2) :: 新手帖 (1) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-09-24
asme2u 写道
mercyblitz 写道
asme2u 写道
import java.util.concurrent.atomic.AtomicBoolean; /** * @author asme2u */ public class Lock { private AtomicBoolean lock = new AtomicBoolean(true); public boolean tryLock() { return lock.compareAndSet(true, false); } public void unlock() { lock.set(true); } }
原理:CAS
优点: 速度快,约是ReentrantLock的2-3倍
缺点: 需JDK5.0+ 无条件对象且不可重入 未获取锁时直接调用unlock不抛IllegalMonitorStateException所以代码必须严格保证获取锁后才能调用unlock
适用场景: 不需要条件对象且当ReentrantLock的tryLock影响了你的性能的时候
我的应用场景: 最近项目中通信程序中的客户端的socket长连接的连接池,客户端使用连接具有高并发但占用连接时间非常短的特点,使用这个类代替ReentrantLock,性能提高了3倍左右
你这个有线程安全问题呢,怎么保证在tryLock 和unlock之间的线程安全(状态一直性)?
ReentrantLock 是利用Thread 队列,只有独占线程看到共享资源,pack其他线程,待操作完毕,下一个线程unpack继续操作。同时还有JVM底层的操作。
如果任何线程使用都遵守tryLock返回true后再去unlock,不会有线程安全问题的,compareAndSet是个原子操作
我的天啊, 在同步(互斥)中间执行的代码叫做“临界区”,AtomicX类都是Nonblocking的,怎么取保其他线程不进入“临界区”,这个不算的话。线程安全,还应该保证 Thread的读和写的数据和主存中的一致。你这个类怎么取保? |
|
返回顶楼 | |
发表时间:2010-09-24
asme2u 写道 hellojinjie 写道 asme2u 写道 ellojinjie 写道 性能应该只与线程执有锁的时间和多少个线程竞争该锁有关,,为什么会和线程如何得到锁有关???
你用tryLock,应该是自旋锁这一类的吧,在我的单核的CPU上,恐怕也不会有什么性能的提高。。。 我实际应用场景是高并发,短占用,如果你应用场景从获取锁到释放锁需要很长的时间,性能不会有多少提高 看来是ReentrantLock 里的tryLock太耗时了,才会使得用AtomicBoolean实现的Lock性能比ReentryLock好 正解,只有符合锁占用的时间与获取锁的时间可比拟时,这个类才有意义,所以我列出了适用场景。 你要看情况, 建议楼主看一下为什么要引入偏向锁,就是为了解决CAS延迟问题。http://blogs.sun.com/dave/entry/biased_locking_in_hotspot 你所说的场景不过是你-client的设置罢了,多核CPU再来试试。 |
|
返回顶楼 | |
发表时间:2010-09-24
mercyblitz 写道 asme2u 写道 hellojinjie 写道 asme2u 写道 ellojinjie 写道 性能应该只与线程执有锁的时间和多少个线程竞争该锁有关,,为什么会和线程如何得到锁有关???
你用tryLock,应该是自旋锁这一类的吧,在我的单核的CPU上,恐怕也不会有什么性能的提高。。。 我实际应用场景是高并发,短占用,如果你应用场景从获取锁到释放锁需要很长的时间,性能不会有多少提高 看来是ReentrantLock 里的tryLock太耗时了,才会使得用AtomicBoolean实现的Lock性能比ReentryLock好 正解,只有符合锁占用的时间与获取锁的时间可比拟时,这个类才有意义,所以我列出了适用场景。 你要看情况, 建议楼主看一下为什么要引入偏向锁,就是为了解决CAS延迟问题。http://blogs.sun.com/dave/entry/biased_locking_in_hotspot 你所说的场景不过是你-client的设置罢了,多核CPU再来试试。 看了一下你的链接,谢谢你的提醒,确实有延迟的问题,如果这么考虑,是否CAS都不可靠了呢?这些个原子类什么时候适用? |
|
返回顶楼 | |
发表时间:2010-09-24
asme2u 写道 mercyblitz 写道 asme2u 写道 hellojinjie 写道 asme2u 写道 ellojinjie 写道 性能应该只与线程执有锁的时间和多少个线程竞争该锁有关,,为什么会和线程如何得到锁有关???
你用tryLock,应该是自旋锁这一类的吧,在我的单核的CPU上,恐怕也不会有什么性能的提高。。。 我实际应用场景是高并发,短占用,如果你应用场景从获取锁到释放锁需要很长的时间,性能不会有多少提高 看来是ReentrantLock 里的tryLock太耗时了,才会使得用AtomicBoolean实现的Lock性能比ReentryLock好 正解,只有符合锁占用的时间与获取锁的时间可比拟时,这个类才有意义,所以我列出了适用场景。 你要看情况, 建议楼主看一下为什么要引入偏向锁,就是为了解决CAS延迟问题。http://blogs.sun.com/dave/entry/biased_locking_in_hotspot 你所说的场景不过是你-client的设置罢了,多核CPU再来试试。 看了一下你的链接,谢谢你的提醒,确实有延迟的问题,如果这么考虑,是否CAS都不可靠了呢?这些个原子类什么时候适用? 在你的代码中,那个逻辑判断是可靠的,但是中间的执行代码是线程不安全的。 PS:CAS是可靠的,它是Wait-free的实现,不过它只能针对单个资源安全,不能作为多语句的原子操作。 |
|
返回顶楼 | |
发表时间:2010-09-25
hellojinjie 写道 性能应该只与线程执有锁的时间和多少个线程竞争该锁有关,,为什么会和线程如何得到锁有关???
你用tryLock,应该是自旋锁这一类的吧,在我的单核的CPU上,恐怕也不会有什么性能的提高。。。 jdk5后的并发包中的类基本都是基于自旋锁实现的 也有叫CAS的 以上是个人理解 |
|
返回顶楼 | |
发表时间:2010-09-25
hellojinjie 写道 性能应该只与线程执有锁的时间和多少个线程竞争该锁有关,,为什么会和线程如何得到锁有关???
你用tryLock,应该是自旋锁这一类的吧,在我的单核的CPU上,恐怕也不会有什么性能的提高。。。 单核是没有提高,但是。。。现在单核的运行环境也不太常见。。所以,楼主这个帖子是可行的。。。 |
|
返回顶楼 | |
发表时间:2010-09-25
if(l.tryLock()) { try{ // 这里能有多个线程同时到达? } finally { l.unlock } }mercyblitz 写道 asme2u 写道
mercyblitz 写道
asme2u 写道
hellojinjie 写道
asme2u 写道
ellojinjie 写道
性能应该只与线程执有锁的时间和多少个线程竞争该锁有关,,为什么会和线程如何得到锁有关???
你用tryLock,应该是自旋锁这一类的吧,在我的单核的CPU上,恐怕也不会有什么性能的提高。。。 我实际应用场景是高并发,短占用,如果你应用场景从获取锁到释放锁需要很长的时间,性能不会有多少提高 看来是ReentrantLock 里的tryLock太耗时了,才会使得用AtomicBoolean实现的Lock性能比ReentryLock好 正解,只有符合锁占用的时间与获取锁的时间可比拟时,这个类才有意义,所以我列出了适用场景。 你要看情况, 建议楼主看一下为什么要引入偏向锁,就是为了解决CAS延迟问题。http://blogs.sun.com/dave/entry/biased_locking_in_hotspot 你所说的场景不过是你-client的设置罢了,多核CPU再来试试。 看了一下你的链接,谢谢你的提醒,确实有延迟的问题,如果这么考虑,是否CAS都不可靠了呢?这些个原子类什么时候适用? 在你的代码中,那个逻辑判断是可靠的,但是中间的执行代码是线程不安全的。 PS:CAS是可靠的,它是Wait-free的实现,不过它只能针对单个资源安全,不能作为多语句的原子操作。
|
|
返回顶楼 | |
发表时间:2010-09-25
我在我电脑上做了一个测试,两者差别没有楼主说的那么大,前者之比后者提高10%左右,我看了下JDK1.6的API源码
前者调用方法: public final boolean compareAndSet(boolean expect, boolean update) { int e = expect ? 1 : 0; int u = update ? 1 : 0; return unsafe.compareAndSwapInt(this, valueOffset, e, u); } 后者调用方法: final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } 两者的源码并没有本质区别呀,所以为什么会有lz描述的那种情况,而我测试也没有这种情况,我CPU 双核,Windows |
|
返回顶楼 | |
发表时间:2010-09-25
rocketball 写道 我在我电脑上做了一个测试,两者差别没有楼主说的那么大,前者之比后者提高10%左右,我看了下JDK1.6的API源码
前者调用方法: public final boolean compareAndSet(boolean expect, boolean update) { int e = expect ? 1 : 0; int u = update ? 1 : 0; return unsafe.compareAndSwapInt(this, valueOffset, e, u); } 后者调用方法: final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } 两者的源码并没有本质区别呀,所以为什么会有lz描述的那种情况,而我测试也没有这种情况,我CPU 双核,Windows 同意楼上的, 以下是我分别在JDK5和JDK6下做的测试. 测试环境: os: Windows XP Professional 2002 sp3; cpu: Pentium(R) Dual-Core CPU E5300 @ 2.60GHz 2.60GHz; mem: 2G; 测试结果如下: JDK5 Lock: 300000000 57359 ReentrantLock: 300000000 56625 JDK6 Lock: 300000000 40734 ReentrantLock: 300000000 55453 LZ , 你测试的结果也贴出来看看. |
|
返回顶楼 | |
发表时间:2010-09-25
rocketball 写道 我在我电脑上做了一个测试,两者差别没有楼主说的那么大,前者之比后者提高10%左右,我看了下JDK1.6的API源码 前者调用方法: public final boolean compareAndSet(boolean expect, boolean update) { int e = expect ? 1 : 0; int u = update ? 1 : 0; return unsafe.compareAndSwapInt(this, valueOffset, e, u); } 后者调用方法: final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } 两者的源码并没有本质区别呀,所以为什么会有lz描述的那种情况,而我测试也没有这种情况,我CPU 双核,Windows 我在公司里的开发环境(XP,双核)上测试确实是10%左右,但在CentOS X86_64 5.4 4核的服务器上跑上面的那个测试程序,大概相差2倍多,在家里的笔记本上xp, 单核的大概也是2倍多,刚才我也翻了下JDK1.6的代码,发现确实没有什么本质区别,我想原因可能在于我的测试程序(第一页里)和我的应用环境(加锁,判断,如果符合条件修改一个标志位,然后解锁)中从获取锁到释放锁的操作非常快以至于tryLock本身的操作所占的比重比较大。至于双核的XP上为什么只有10%,没想明白 |
|
返回顶楼 | |