本文共 1516 字,大约阅读时间需要 5 分钟。
最近想复习下多线程相关知识,尝试手动造了一个reentrantLock的轮子,
计划根据原理先实现最初级的版本,然后不断重构,最终达到类似原版的效果。源码地址:
第一版本:实现基本功能,双向链表实现加锁解锁阻塞
reentrantLock的原理是: 1、维护一个双向链表保存被阻塞的线程,链表的每一个节点保存的是线程封装的Node节点对象, 2、如果线程取不到锁,就把线程加入到链表尾部 3、已获取到锁的线程去解锁时,就从链表头部取出一个阻塞线程唤醒 实现代码: (参考com.bones.locks.myReenterLock.MyReentrantLock)这样就初步实现了reentrantLock的功能。
存在的问题 线程1加锁, 线程2阻塞时能正常工作 线程1加锁,线程2,线程3阻塞时不能正常工作 这是一个典型的并发问题,因为线程2,3是并行的,对共享变量的操作不能保存原子性第二版本: 修复bug,操作共享变量时加synchronized关键字保证原子性
实现代码: (参考com.bones.locks.myReenterLock.MyReentrantLockV2) 经过多次测试,每次都能执行成功, 有时候是线程2先获得锁,有时候是线程3先获得锁第三版本:性能优化,用volatile+atomic替换synchroneized
实现代码: (参考com.bones.locks.myReenterLock.MyReentrantLockV3) 测试后发现性能提高的效果不明显 为了验证性能提高,专门写了一个测试类 参考 com.bones.locks.myReenterLock.TestAtomic 实际测试,发现把synchronized 换成atomicInteger速度有提高,但是不明显,1000个线程,累加1000次的情况下,执行时间从280毫秒优化到140毫秒。4.0版本 实现公平锁,设计模式优化,抽取Sync
第一层抽象,notfairSync和fairSync 公平锁和非公平锁的区别就在于是获取锁时否判断队列中是否有等待线程。 非公平锁不判断等待队列,直接获取锁,有机会先执行。 公平锁需要判断等待队列,等待他的前置节点唤醒他。 如果非公平锁没获取到锁进入队列了,行为就跟公平锁一样了。 因为需要实现公平锁和非公平锁2个版本, 把reentrantLock类改造为notfairSync和fairSync两个类分别实现公平锁和非公平锁 面向接口编程,定义抽象锁Sync,公平锁fairSync和非公平锁notfairSync都是Sync的实现类 reentrantLock做代理类引用Sync public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } reentrantLock.Lock里调用Sync.Lock reentrantLock.unLock里调用Sync.unLock5.0版本,实现读写锁信号量,设计模式优化,抽取AQS
第二层抽象,AQS 如果只实现重入锁,Sync已经很完美了, 但是还要实现读写锁和信号量, 他们的不同点主要在于state 重入锁的state只有1和0, 读写锁的state分高低位, 信号量的state从0改变到1要经过多次lock, 他们的相同点是都有公平锁和非公平锁的概念,都可以抽象到Sync这个层级,而他们的Sync有共同点,阻塞队列的数据结构一致 所以Sync还可以继续抽象一层AQS,包含共用的阻塞队列,转载地址:http://rvhwi.baihongyu.com/