java进阶-7-D -多线程-Lock专题- ReentrantLock类
开篇一张,故事全靠~~~:根据AQS来自己实现一个Lock吧
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* @author Shengwuyou
* @data 2019/3/1 0001 14:35
*/
public class SelfLock implements Lock {
private static Sync sync = new Sync();
private static class Sync extends AbstractQueuedSynchronizer{
/**
* 判断当前线程是否是独占式获取锁
* @return
*/
@Override
protected boolean isHeldExclusively() {
//判断当前运行的线程 和 占据着锁的线程是否一致 下面的代码
return getExclusiveOwnerThread() == Thread.currentThread();
}
/**
* 尝试获取锁的判断逻辑也是非常简单,判断当前Lock的状态state n = 0表示还没有线程占用锁, n=1表示有一个线程占用了 ,当 n大于1的时候表示该锁 重入了n次
* 这里对于重入可多少个锁互相竞争锁是没有关系的,重入例子 ==> Lock{ Lock{}finally{unlock} }finally{unlock} 此时n=2
* @param arg
* @return
*/
@Override
protected boolean tryAcquire(int arg) {
int state = getState();
if (state == 0){
if (compareAndSetState(state,arg)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
} else if (state > 0 && isHeldExclusively()){
if (compareAndSetState(state,state+arg)) {
return true;
}
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
int expState = getState() - arg;
if (isHeldExclusively()){
throw new IllegalThreadStateException();
}else if (expState == 0 ){
setExclusiveOwnerThread(null);
}
setState(expState);
return true;
}
public Condition newCOndition(){
return new ConditionObject();
}
}
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,time);
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCOndition();
}
}
一眼看下来其实非常简单!就tryAcquire 和 tryRelease 这2个方法,而且里面要做的就是去修改一个参数state 锁的状态:我们在自定义锁的时候定义这个state的规则:0表示锁未被任何线程占用 ,>0 表示被某一个线程占用了,设定 步长是1,表示占用的线程每重入一次state就加一。
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author Shengwuyou
* @data 2019/3/1 0001 15:31
*/
public class SelfLockTest {
public static void main(String[] args) {
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3,5,60,
TimeUnit.SECONDS,new LinkedBlockingQueue<>(30), new ThreadPoolExecutor.DiscardOldestPolicy());
SelfLock lock = new SelfLock();
Runnable run = ()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " : --- 休眠3秒");
Thread.sleep(3000);
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " : ---重入再次 休眠3秒");
Thread.sleep(3000);
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
};
for (int i = 0;i <10; i++){
poolExecutor.execute(run);
}
poolExecutor.shutdown();
}
}
测试的结果喜人,这里我没有将state的变化打印出来但是有兴趣的小伙伴可以加到selfLock中,或者debug查看
从fairSync 和 nonfairSync ---开始介绍ReentrantLock:
对于公平 / 非公平 获取锁 ,区别点就是 公平锁:新任务进来必须加入到等待队列之后让等的最久的那个任务先执行 非公平锁 :新任务进来先去尝试能够获取到锁,不行在加入到等待队列的尾巴上。
在我们分析之后非公平锁是会比公平锁在性能上有优势,但是碰到执行任务的时间花费很长的情况,2种锁的差距会缩小到可以忽略,但是公平锁能够保证任务的先进先出,这个优势是很多地方需要用到的
非公平锁和公平锁的代码实现区别就一点 :在 Lock的时候
NonFair 会在当前没有线程占有锁的时候 直接去判断能够拿到锁,能得话就over --------上面的例子是非公平锁
Fair 会在当前没有线程占有锁的时候 判断一下等待队列中是否有Node,有的话那就加入到等待队列尾部
代码差异也是非常简单这里就不讲了