AQS实战-自定义同步组件TwinsLock

本文介绍了如何设计一个名为TwinsLock的同步工具,它在同一时刻仅允许两个线程访问。TwinsLock基于自定义同步器AbstractQueuedSynchronizer实现,通过共享式访问控制状态,利用CAS操作保证线程安全。在测试中,展示了多个线程并发访问时,TwinsLock能有效限制同一时刻只有两个线程持有锁,从而达到预期的同步效果。
摘要由CSDN通过智能技术生成

背景

设计一个同步工具:该工具在同一时刻,只允许至多两个线程同时访问,超过两个线程的访问将被阻塞,我们将这个同步工具命名为TwinsLock。

实现

确定访问模式

TwinsLock能够在同一时刻支持多个线程的访问,这显然是共享式访问,因此,需要使用同步器提供的acquireShared(int args)方法等和Shared相关的方法,这就要求TwinsLock必须重写tryAcquireShared(int args)方法和tryReleaseShared(int args)方法,这样才能保证同步器的共享式同步状态的获取与释放方法得以执行。

定义资源数

TwinsLock在同一时刻允许至多两个线程的同时访问,表明同步资源数为2,这样可以设置初始状态status为2,当一个线程进行获取,status减1,该线程释放,则status加1,状态的合法范围为0、1和2,其中0表示当前已经有两个线程获取了同步资源,此时再有其他线程对同步状态进行获取,该线程只能被阻塞。在同步状态变更时,需要使用compareAndSet(int expect,int update)方法做原子性保障

组合自定义同步器

自定义同步组件通过组合自定义同步器来完成同步功能,一般情况下自定义同步器会被定义为自定义同步组件的内部类

代码实现如下所示:

public class TwinsLock implements Lock {

    private final Sync sync = new Sync(2);

    private static final class Sync extends AbstractQueuedSynchronizer {
        Sync(int count) {
            if (count <= 0) {
                throw new IllegalArgumentException("count must large than zero");
            }
            setState(count);
        }

        @Override
        public int tryAcquireShared(int reduceCount) {
            for (; ; ) {
                int current = getState();
                int newCount = current - reduceCount;
                if (newCount < 0 || compareAndSetState(current, newCount)) {
                    return newCount;
                }
            }
        }

        @Override
        public boolean tryReleaseShared(int returnCount) {
            for (; ; ) {
                int current = getState();
                int newCount = current + returnCount;
                if (compareAndSetState(current, newCount)) {
                    return true;
                }
            }
        }
    }

    @Override
    public void lock() {
        sync.acquireShared(1);
    }

    @Override
    public void unlock() {
        sync.releaseShared(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 Condition newCondition() {
        return null;
    }
}

TwinsLock实现了Lock接口,提供了面向使用者的接口,使用者调用lock()方法获取锁,随后调用unlock()方法释放锁,而同一时刻只能有两个线程同时获取到锁

TwinsLock同时包含了一个自定义同步器Sync,而该同步器面向线程访问和同步状态控制。

以共享式获取同步状态为例:同步器会先计算出获取后的同步状态,然后通过CAS确保状态的正确设置,当tryAcquireShared(int reduceCount)方法返回值大于等于0时,当前线程才能获取同步状态,对于上层的TwinsLock而言,则表示当前线程获得了锁

同步器作为一个桥梁,连接线程访问以及同步状态控制等底层技术。

测试

测试验证TwinsLock是否能按照预期工作。在测试用例中定义了工作线程Worker,该线程在执行过程中获取锁,当获取锁之后使当前线程睡眠1秒(并不释放锁),随后打印当前线程名称,最后再次睡眠1秒并释放锁,测试用例代码如下:

public class TwinsLockTest {
    @Test
    public void test(){
        final Lock lock = new TwinsLock();
        class Worker extends Thread{
            @Override
            public void run(){
                while(true){
                    lock.lock();
                    System.out.println("current thread name"+Thread.currentThread().getName()+"拿到了锁");
                    try {
                        Thread.sleep(1000);
                        System.out.println("current thread name:"+Thread.currentThread().getName());
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                        System.out.println("current thread name"+Thread.currentThread().getName()+"释放了锁");
                    }
                }
            }
        }

        //启动10个线程
        for(int i=0;i<10;i++){
            Worker worker = new Worker();
            worker.setDaemon(true);
            worker.start();
        }

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }
}
current thread nameThread-0拿到了锁
current thread nameThread-1拿到了锁
current thread name:Thread-1
current thread name:Thread-0
current thread nameThread-0释放了锁
current thread nameThread-1释放了锁
current thread nameThread-2拿到了锁
current thread nameThread-3拿到了锁
current thread name:Thread-2
current thread name:Thread-3
current thread nameThread-2释放了锁
current thread nameThread-3释放了锁
current thread nameThread-5拿到了锁
current thread nameThread-4拿到了锁
current thread name:Thread-5
current thread name:Thread-4

运行测试用例,发现线程成对输出,并且在同一时刻只有两个线程能够获取到锁,测试成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值