实现自定义锁需要考虑的事情:
1. 锁的是线程,争用,首先有一个变量,用于表示锁与未锁的状态,还需要标识出是哪个线程获得了锁,其它线程等待的队列
2. 线程阻塞,精确唤醒指定线程,使用LockSupport
3. 原子性操作状态CAS
4. 线程排队,使用队列(链路)
# 一、JUC中Lockjuc中的Lock定义方法如下:
具体的实现有:
以ReentrantLock为例,默认使用的是非公平锁进行加锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//已经加锁失败,为何还要继续尝试加锁?
//A:线程在park后恢复,需要继续尝试获取锁
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
仿照上面的说明,实现了一把简单的锁,比不上JUC中的实现,仅供进行原理学习
package com.zte.sunquan.demo.lock;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.locks.LockSupport;
import lombok.Data;
import sun.misc.Unsafe;
@Data
public class SQLock {
//private AtomicInteger state=new AtomicInteger(1);
private static final Unsafe unsafe = UnsafeInstance.getInstance();
private static final long stateOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset(SQLock.class.getDeclaredField("state"));
} catch (Exception e) {
throw new Error(e);
}
}
/**
* 锁状态
*/
private volatile int state = 0;
/**
* 当前获得锁的线程
*/
private Thread lockHolder;
/**
* 线程缓存队列
*
* @return
*/
private Queue<Thread> queue = new ConcurrentLinkedDeque<>();
/**
* 尝试加锁
*
* @return
*/
private boolean tryAcquire() {
int state = getState();
Thread thread = Thread.currentThread();
if (state == 0) {
//比较两种写法的不同
//queue.add不应该写,应该增加线程到队列只在Lock里进行
//先排队-第一个则可以买票
// if (!queue.isEmpty() && queue.peek() != thread) {
// queue.add(thread);
// } else {
// if (compareAndSwapState(state, 1)) {
// //记录当前线程拿到锁
// setLockHolder(thread);
// return true;
// }
// }
if ((queue.isEmpty() || thread == queue.peek()) && compareAndSwapState(0, 1)) {
setLockHolder(thread);
return true;
}
}
return false;
}
/**
* 加锁
*/
public void lock() {
if (tryAcquire()) {
System.out.println("lock Thread-name:" Thread.currentThread().getName());
return;
}
Thread thread = Thread.currentThread();
queue.add(thread);//何时移除?
//未拿到锁的线程阻塞(或自旋-占CPU for(;;)) 为何不直接阻塞?
for (; ; ) {
if (queue.peek() == thread && tryAcquire()) {//直接去拿,非公平,先排队再买票
System.out.println("Thread-name:" thread.getName());
queue.poll();
return;
}
//解锁后需要唤醒,唤醒后立即再次尝试获得锁
LockSupport.park(thread);
}
}
/**
* 解锁
*/
public void unLock() {
//只能当前持久锁的线程解锁,其它线程无权解锁
Thread thread = Thread.currentThread();
if (thread != lockHolder) {
throw new RuntimeException("Can not unlock.");
}
int state = getState();
if (compareAndSwapState(state, 0)) {
System.out.println(String.format("unlock success:%s", thread.getName()));
setLockHolder(null);//无线程持有锁
Thread head = queue.peek();
if (head != null) {
LockSupport.unpark(head);
}
}
}
private boolean compareAndSwapState(int oldVal, int newVal) {
return unsafe.compareAndSwapInt(this, stateOffset, oldVal, newVal);
}
public void printQueue() {
System.out.println(this.queue.size());
}
}
package com.zte.sunquan.demo.lock;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class UnsafeInstance {
private UnsafeInstance() {
}
public static Unsafe getInstance() {
return Singleton.unsafe;
}
private static class Singleton {
private static Unsafe unsafe;
static {
try {
Field getUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
getUnsafe.setAccessible(true);
unsafe = (Unsafe) getUnsafe.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
测试用例:
package com.zte.sunquan.demo.lock;
import java.util.concurrent.CountDownLatch;
public class SQLockTest {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
SQLock lock=new SQLock();
CountDownLatch latch = new CountDownLatch(100);
for (int x=0;x<100;x ){
new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println("start:" Thread.currentThread().getName());
lock.lock();
for (int i = 0; i < 100; i ) {
count ;
}
//System.out.println("bbb:" Thread.currentThread().getName());
//lock.unLock();
}).start();
latch.countDown();
}
Thread.sleep(500);
System.out.println("total:" count);
lock.printQueue();
}
}