1、AQS
1、概念:AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒 时锁分配的机制,这个机制 AQS 是用 CLH 队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
2、简单的AQS图解:
3、实现案例
4、AQS 使用一个 int 成员变量state来表示同步状态,这个成员变量是volatitlex修饰的,通过内置的 FIFO 队列来完成获取资源线程的排队工作。AQS 使用 CAS 对该同步状态进行原子操作实现对其值的修改。
// 定义共享资源状态
private volatile int state;
// 获取共享资源的状态
protected final int getState() {
return state;
}
// 设置共享资源的状态
protected final void setState(int newState) {
state = newState;
}
// CAS竞争state
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
以ReentrantLock为例,state初始化为0,表示未锁定状态。
A线程lock时,会调用tryAcquire独占该锁并将state+1。此后,其他线程再tryAcquire时就会失败,直到A线程unlock到state=0(即释放锁)为止,其它线程才有机会获取该锁。
当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。
5、关于线程的阻塞策略
2、ReentrantLock
ReentrantLock 支持公平锁和非公平锁,可重入锁ReentrantLock的底层是通过 AQS实现。
ReentrantLock 类内部总共存在Sync、NonfairSync、FairSync三个类,NonfairSync与 FairSync类继承自 Sync类,Sync类继承自 AbstractQueuedSynchronizer抽象类。下面逐个进行分析
ReentrantLock与Synchronized的区别:
ReentrantLock的使用:
// ReentrantLock的构造函数,默认使用非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
// ReentrantLock使用demo
public void test () throw Exception {
// 1.初始化选择公平锁、非公平锁
ReentrantLock lock = new ReentrantLock(true);
// 2.可用于代码块
lock.lock();
try {
try {
// 3.支持多种加锁方式,比较灵活
// 4.具有可重入特性
if(lock.tryLock(100, TimeUnit.MILLISECONDS)){ }
} finally {
// 5.手动释放锁
lock.unlock()
}
} finally {
lock.unlock();
}
}
ReentrantLock实现可重入:
// 1、非公平锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
// 获取锁成功,设置同步器exclusiveOwnerThread为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
// 同步器exclusiveOwnerThread为当前线程,仅累加同步状态
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
// 2、公平锁
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
// c & 0x0000FFFF,查询写状态值
int w = exclusiveCount(c);
if (c != 0) {
// 状态值 != 0 && 写状态值 == 0,表示有当前有读锁
// 独占线程是当前线程,可重入
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
3、练习
3.1 给三个文件,每个文件里面有10万条记录每一行是一个0-100000之间的随机数 我们通过启动三个线程 来统计下这个文件里面大于 95000 数字有多少?
public class MyTest {
public static void main(String[] args) throws InterruptedException {
List<Long> array1 = getRandomArray();
List<Long> array2 = getRandomArray();
List<Long> array3 = getRandomArray();
MyCounter myCounter = new MyCounter();
Thread thread1 = new Thread(new Runner(myCounter, array1));
Thread thread2 = new Thread(new Runner(myCounter, array2));
Thread thread3 = new Thread(new Runner(myCounter, array3));
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
System.out.println("统计大于95000的数字共有:" + myCounter.getSum());
}
static class MyCounter {
ReentrantLock reentrantLock = new ReentrantLock(true);
protected volatile Integer sum = 0;
public void add(int value) {
reentrantLock.lock();
try {
this.sum = this.sum + value;
} catch (Throwable throwable) {
reentrantLock.unlock();
} finally {
reentrantLock.unlock();
}
}
public Integer getSum() {
return this.sum;
}
}
static class Runner implements Runnable{
private MyCounter counter;
private List<Long> data;
Runner(MyCounter counter, List<Long> data) {
this.counter = counter;
this.data = data;
}
@Override
public void run() {
for (int i = 0; i < data.size(); i++) {
if (data.get(i) > 95000) {
counter.add(1);
}
}
}
}
private static List<Long> getRandomArray() {
List<Long> result = Lists.newArrayList();
Random random = new Random();
for (int j = 1; j <= 100000; j++) {
int i = random.nextInt(100000);
result.add(Long.valueOf(i));
}
return result;
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。