AQS ( AbstractQueuedSynchronizer) 抽象队列同步器
通过源码我们可以看到其实AQS是使用了CAS,保证原子性。
那么CAS到底是什么呢?
CAS:Compare and Swap,即比较再交换。
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该 位置的值。
所有CAS其实是操作内存,但是java是不能直接操作内存区域,所有又有了一个类叫Unsafe,java的Unsafe类为我们提供了类似C++可以操作内存。
unsafe类是"final"的,不允许继承。且构造函数是private的
public final class Unsafe {
private static final Unsafe theUnsafe;
public static final int INVALID_FIELD_OFFSET = -1;
private static native void registerNatives();
// 构造函数是private的,不允许外部实例化
private Unsafe() {
}
...
}
所以我们无法通过new 的方式来实例化,那么我们要如何实例化这个类?
其实我们java也可以通过反射来实例化对象,现在我们利用反正来实例化unsafe
package com.gpdi.dispatch.config;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* @Author Lxq
* @Date 2020/3/8 15:42
* @Version 1.0
*/
public class UnsafeInstance {
public static Unsafe reflectGetUnsafe() {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
那么我们接下来就实现一个独占锁
package com.gpdi.dispatch.config;
import sun.misc.Lock;
import sun.misc.Unsafe;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.locks.LockSupport;
/**
* @Author Lxq
* @Date 2020/3/8 15:27
* @Version 1.0
*/
public class AQSLock {
/**
* 当前加锁的状态
*/
private volatile int state = 0;
/**
* 当前持有锁的线程
*/
private Thread lockHolder;
private static ConcurrentLinkedDeque<Thread> waiters = new ConcurrentLinkedDeque<>();
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public Thread getLockHolder() {
return lockHolder;
}
public void setLockHolder(Thread lockHolder) {
this.lockHolder = lockHolder;
}
/**
* 加锁
*
* @return
*/
public Boolean aquire() {
// cas比较交换-原子算法
Thread current = Thread.currentThread();
// 初始状态
int c = getState();
if (c == 0) {
// 同步器还没有被持有
if ((waiters.size() == 0 || current == waiters.peek()) && compareAndSwapStatu(0, 1)) {
setLockHolder(current);
//加锁成功,直接返回
return true;
}
}
return false;
}
public void lock() {
if (aquire()) {
// 加锁成功
return;
}
Thread current = Thread.currentThread();
// 将加锁不成功的线程放入队列中
waiters.add(current);
// 自旋
for (; ; ) {
if ((current == waiters.peek()) && aquire()) {
waiters.poll();
return;
}
// 阻塞当前线程
LockSupport.park(current);
}
}
public void unLock() {
// 当前线程是否是锁持有的线程
if (Thread.currentThread() != lockHolder) {
throw new RuntimeException("lockholder is not current thread");
}
int state = getState();
if (compareAndSwapStatu(state, 0)) {
setLockHolder(null);
// 唤醒队列中线程
Thread first = waiters.peek();
if (first != null) {
LockSupport.unpark(first);
}
}
}
/**
* 原子操作
*
* @param except
* @param update
* @return
*/
private final Boolean compareAndSwapStatu(int except, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, except, update);
}
private static final Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();
private static long stateOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset(AQSLock.class.getDeclaredField("state"));
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
创建一个测试,压测一下
package com.gpdi.dispatch.service;
import com.gpdi.dispatch.config.AQSLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author Lxq
* @Date 2020/3/8 16:29
* @Version 1.0
*/
@Service
public class TradeService {
@Autowired
private JdbcTemplate jdbcTemplate;
private static AQSLock aqs = new AQSLock();
private static Lock lock = new ReentrantLock();
public String descStockNoLock() {
aqs.lock();
Integer stock;
List<Map<String, Object>> result = jdbcTemplate
.queryForList("select * from shop_order where id = 1");
if (result == null || result.isEmpty() || (stock = (Integer) result.get(0).get("stock")) <= 0) {
System.out.println("下单失败,已经没有库存了");
aqs.unLock();
return "下单失败,已经没有库存了";
}
stock--;
jdbcTemplate.update("update shop_order set stock = ? where id = 1",stock);
System.out.println("下单成功,当前剩余------》" + stock);
aqs.unLock();
return "下单成功,当前剩余-----》" + stock;
}
}
创建压测接口
package com.gpdi.dispatch.controller;
import com.gpdi.dispatch.service.TradeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author Lxq
* @Date 2020/3/8 16:34
* @Version 1.0
*/
@RestController
@RequestMapping("/aqs")
public class AQSTestController {
@Autowired
private TradeService tradeService;
@RequestMapping("/stock")
public void testAqs(){
tradeService.descStockNoLock();
}
}
利用Jmeter,压测