自定义同步锁的实现
本文出自《java 并发编程的艺术》
在多线程访问资源的时候分为独占式与共享式,本例子是一个基于AbstractQueuedSynchronizer的自定义同步锁;
关于AbstractQueuedSynchronizer的实现原理,其实就是内部维护了一个双向队列,在本例子中,如果线程未获取到锁的时候会发生自旋。
一下是自定义同步锁的实现:
package com.kangxin.doctor.connectdemo;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class TwinsLock implements Lock {
private final Sync sync = new Sync(2);
@Override
public void lock() {
sync.acquireShared(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void unlock() {
sync.releaseShared(1);
}
@Override
public Condition newCondition() {
return null;
}
private static final class Sync extends AbstractQueuedSynchronizer{
Sync(int count) {
if (count <=0){
throw new IllegalArgumentException("count must large than zero.");
}
setState(count);
}
@Override
protected int tryAcquireShared(int reduceCount) {
for (;;){
int current = getState();
int newCount = current - reduceCount;
if (newCount < 0 || compareAndSetState(current, newCount)){
return newCount;
}
}
}
@Override
protected boolean tryReleaseShared(int returnCount) {
for (;;){
int current = getState();
int newCount = current + returnCount;
if (compareAndSetState(current, newCount)){
return true;
}
}
}
}
}
可以看到在这个类中有一个自定义的内部类的实现,该Sync实现了AbstractQueuedSynchronizer的tryAcquireShared方法,由此可知线程的访问模式是共享式,只不过要根据构造传入的count来控制共享的线程数量,假设传入的是2的话,那么只允许两个线程共享资源,其他的处于阻塞;
下面通过一个例子来说明一下,这个例子创建来10个线程,只允许两个线程去共享资源,当前线程处理完操作之后释放锁, 好让其他阻塞的线程来访问资源。
package com.kangxin.doctor.connectdemo;
import java.util.concurrent.locks.Lock;
public class ShareSyncTest {
public static void main(String[] args) {
final Lock lock = new TwinsLock();
class Worker extends Thread{
@Override
public void run() {
while (true){
lock.lock();
try {
SleepUtil.second(1);
System.out.println(Thread.currentThread().getName());
SleepUtil.second(1);
}finally {
lock.unlock();
}
}
}
}
for (int i=0; i < 10; i++){
Worker w= new Worker();
w.setDaemon(true);
w.start();
}
for (int i=0; i < 10; i++){
SleepUtil.second(1);
System.out.println();
}
}
}
从代码这可以看到在线程里面我们用来一个循环类处理,开始的时候获取锁然后睡眠来1秒之后打印来一下线程名字然后再睡眠一秒之后释放了锁;该示例运行起来之后会发现每隔2秒会同时打印2个线程信息。
效果如下
Thread-0
Thread-1
Thread-1
Thread-0
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1