前言
使用CAS尝试简单手写锁
一、手写锁需要注意什么?
手写锁的话需要注意,使用cas操作抢锁;线程为唤醒问题;锁的声明需要使用AtomicReference,不然会出现原子性问题
二、手写锁示范
1.锁的逻辑说明
1、首先两个线程去抢锁,用cas抢锁,线程成功则获取到锁,失败则挂起,挂起后进去队列,等待成功的线程释放锁资源;然后队列中的线程,头部的锁继续去cas抢锁,以此循环
2.代码示例
代码如下(示例):
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
public class TestLock implements Lock {
//表示谁获得了当前锁,抢锁抢的就是owner
AtomicReference<Thread> owner = new AtomicReference<>();
//等待队列
private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>();
@Override
public void lock() {
if(!tryLock()){
//抢锁失败则进入等待队列
waiters.offer(Thread.currentThread());
//用死循环防止伪唤醒问题
for (;;){
//取出队列头部,但是不出队列
Thread head = waiters.peek();
//如果队列头部线程==当前线程,则抢锁,失败则挂起
if(head == Thread.currentThread()){
// 头部线程抢锁失败,则挂起,成功则线程出队列
if(!tryLock()){
LockSupport.park();
}else{
//线程出队列
waiters.poll();
return;
}
}else{
//不是队列头,则挂起线程
LockSupport.park();
}
}
}
}
@Override
public boolean tryLock() {
//CAS操作对比线程是否为空,为空则将owner设为当前线程,并返回true
return owner.compareAndSet(null, Thread.currentThread());
}
@Override
public void unlock() {
if(owner.compareAndSet(Thread.currentThread(), null)){
Thread head = waiters.peek();
if(head != null){
//唤醒头部线程
LockSupport.unpark(head);
}
}
}
@Override
public Condition newCondition() {
return null;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
}
总结
手写锁的话需要注意,使用cas操作抢锁;线程为唤醒问题;锁的声明需要使用AtomicReference,不然会出现原子性问题