基于自定义同步器实现生产-消费模型

86 阅读2分钟

一、自定义同步器

  • 原子变量state的含义:state=0表示目前锁没有被线程持有,state=1表示锁已被某线程持有
  • 不可重入锁
  • 支持条件变量

二、自定义同步器代码实现

public class NonReentrantLock implements Lock, Serializable {

    // 内部工具类
    private static class Sync extends AbstractQueuedSynchronizer{
        //判断是否锁已经被持有
        @Override
        protected  boolean isHeldExclusively(){
            return getState()==1;
        }

        //如果state=0,尝试获取锁
        @Override
        public boolean tryAcquire(int acquires){
            assert acquires==1;
            if (compareAndSetState(0,1)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        // 尝试释放锁,设置state为0
        @Override
        protected boolean tryRelease(int releases){
            assert releases==1;
            if (getState() == 0){
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        // 提供条件变量接口
        Condition newCondition(){
            return new ConditionObject();
        }
    }

    // 创建一个Sync来做具体的工作
    private  final Sync sync = new Sync();
    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1,unit.toNanos(time));
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}

三、生产-消费模型实现

@Slf4j
public class Test<i> {
    // 创建不可重入锁
    final static NonReentrantLock lock = new NonReentrantLock();
    // 创建队列未满的条件变量
    final static Condition notFull = lock.newCondition();
    // 创建队列非空的条件变量
    final static Condition notEmpty = lock.newCondition();
    // 创建阻塞队列
    final static Queue<Integer> queue = new LinkedBlockingDeque<Integer>();
    // 定义阻塞队列大小
    final static int queueSize = 10;


    public static void main(String[] args) {
        // 先把队列填满
        Random random = new Random();

        // 创建生产者线程
        Thread producer = new Thread(new Runnable() {
            @Override
            public void run() {
                // 获取独占锁
                lock.lock();
                try{
                    //如果队列已满
                    while (queue.size() == queueSize){
                        // 放入条件变量队列
                        log.info("把生产线程放入条件变量队列...");
                        notEmpty.await();
                    }
                    // 添加元素到队列
                    int i = random.nextInt(100);
                    queue.add(i);
                    log.info("生产线程把元素{}放入队列",i);
                    //唤醒消费线程
                    log.info("唤醒消费线程...");
                    notFull.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        });
        Thread consumer = new Thread(new Runnable() {
            @Override
            public void run() {
                // 获取独占锁
                lock.lock();
                try{
                    // 队列空,放入条件变量等待队列
                    while (queue.size() == 0){
                        log.info("把消费线程放入条件变量队列...");
                        notFull.await();
                    }
                    // 消费一个元素
                   int  consEle = queue.poll();
                    log.info("消费线程消费了元素{}",consEle);
                    // 唤醒生产线程
                    notEmpty.signalAll();
                    log.info("唤醒生产线程...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        producer.start();
        consumer.start();
    }

}

image.png 可以看到,使用条件变量作为同步工具,可以实现生产者/消费者线程的精准唤醒