这个例子不单是让我们更了解AQS的具体使用,还明白了AQS类中state字段的作用
因为tryAcquire这个方法是我们自己实现的,方法作用是尝试获取资源,根据我们自己的逻辑来决定是否获取成功.
像下面这个例子,我的逻辑就是当资源小于0时,线程就获取失败,所以我先调用setState方法设初始值放到state字段,值为N,然后每来一个线程就将N-1,然后判断是否N-1小于0,从而实现线程资源的控制
工具类
/*
* 设计一个同步工具,该工具在同一时刻,只能有两个线程能够并行访问,超过限制的其他线程进入阻塞状态。
* 对于这个需求,可以利用同步器完成一个这样的设定,定义一个初始状态,为2,一个线程进行获取那么减1,一个线程释放那么加1,状态正确的范围在[0,1,2]三个之间
* 当在0时,代表再有新的线程对资源进行获取时只能进入阻塞状态(注意在任何时候进行状态变更的时候均需要以CAS作为原子性保障)。
* 由于资源的数量多于1个,同时可以有两个线程占有资源
*/
public class QueueSynchronizerTest implements Lock {
private final Sync sync = new Sync(2);
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -7889272986162341211L;
Sync(int count) {
if (count <= 0) {
throw new IllegalArgumentException("count must large than zero.");
}
setState(count);//这里设置state的值,也就是说,其实state的值是我们自己设的
}
//重写这个方法,这个方法返回值小于0代表获取资源失败,反之为获取资源成功
public int tryAcquireShared(int reduceCount) {
for (;;) {
int current = getState();
int result = current - reduceCount;
if (result < 0 || compareAndSetState(current, result)) {
return result;
}
}
}
public boolean tryReleaseShared(int returnCount) {
for (;;) {
int current = getState();
int result = current + returnCount;
if (compareAndSetState(current, result)) {
return true;
}
}
}
}
@Override
public void lock() {
sync.acquireShared(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquireShared(1) >= 0;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
sync.releaseShared(1);
}
@Override
public Condition newCondition() {
return null;
}
}
实验类
public class TwinsLockTest {
public static void main(String[] args){
final Lock lock = new QueueSynchronizerTest();
class Worker extends Thread {
public void run() {
lock.lock();
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "," + Thread.currentThread());
Thread.sleep(1000);
} catch (Exception ex) {
System.out.println("T1");
ex.printStackTrace();
} finally {
lock.unlock();
}
}
}
for (int i = 0; i < 10; i++) {
System.out.println("B" + i);
Worker w = new Worker();
w.start();
}
}
}
控制台输出
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
Thread-0,Thread[Thread-0,5,main]
Thread-3,Thread[Thread-3,5,main]
Thread-4,Thread[Thread-4,5,main]
Thread-7,Thread[Thread-7,5,main]
Thread-8,Thread[Thread-8,5,main]
我们可以看到,控制台隔一段时间才会显示两个线程,这是因为,我们限制了每次获取资源的,只能2个线程,当超过两个线程获取资源时,多出的线程会被放到队列里去