MST

星途 面试题库

面试题:Java中ArrayBlockingQueue公平性与非公平性的区别

在Java的ArrayBlockingQueue中,公平性模式与非公平性模式在获取锁和元素操作上有什么不同?请简要阐述,并举例说明在生产者 - 消费者场景下这两种模式可能带来的影响。
23.7万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

公平性模式与非公平性模式在获取锁和元素操作上的不同

  1. 公平性模式
    • 获取锁:遵循FIFO(先进先出)原则,等待时间最长的线程会优先获取锁。这意味着在队列的锁竞争中,线程按照排队顺序依次获取锁。
    • 获取元素:当从ArrayBlockingQueue获取元素(如调用take方法)时,等待时间最长的消费者线程会优先获得元素。
  2. 非公平性模式
    • 获取锁:不保证等待时间最长的线程优先获取锁,新到来的线程有可能在等待队列中的线程之前获取锁。这种方式在一定程度上可以提高系统的吞吐量,但可能导致某些线程长时间等待(饥饿现象)。
    • 获取元素:在从ArrayBlockingQueue获取元素时,新到来的消费者线程有可能在等待队列中的其他消费者线程之前获取到元素,而不考虑等待顺序。

生产者 - 消费者场景下的影响

  1. 公平性模式
    • 优点:每个消费者线程都能按照其等待顺序获取元素,不会出现某个消费者线程长时间等待的情况,公平性较好。
    • 缺点:由于需要严格按照顺序处理,在高并发场景下,频繁的线程切换可能会带来一定的性能开销,导致整体吞吐量相对较低。
    • 示例:假设有多个消费者线程等待从ArrayBlockingQueue中获取元素,公平性模式下,最早进入等待队列的消费者线程会最先获取到元素,无论后续是否有新的消费者线程加入。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class FairProducerConsumer {
    private static final int QUEUE_CAPACITY = 5;
    private static final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY, true);
    private static final Lock lock = new ReentrantLock(true);
    private static final Condition notFull = lock.newCondition();
    private static final Condition notEmpty = lock.newCondition();

    static class Producer implements Runnable {
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                try {
                    lock.lock();
                    while (queue.size() == QUEUE_CAPACITY) {
                        notFull.await();
                    }
                    queue.put(i);
                    System.out.println("Produced: " + i);
                    notEmpty.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    static class Consumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    lock.lock();
                    while (queue.isEmpty()) {
                        notEmpty.await();
                    }
                    int item = queue.take();
                    System.out.println("Consumed: " + item);
                    notFull.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread producerThread = new Thread(new Producer());
        Thread consumer1Thread = new Thread(new Consumer());
        Thread consumer2Thread = new Thread(new Consumer());

        producerThread.start();
        consumer1Thread.start();
        consumer2Thread.start();
    }
}
  1. 非公平性模式
    • 优点:新到来的线程有机会快速获取锁和元素,减少线程切换次数,在高并发场景下能提高系统的吞吐量。
    • 缺点:可能导致某些线程长时间无法获取锁和元素,出现线程饥饿现象。
    • 示例:同样在多个消费者线程等待从ArrayBlockingQueue获取元素的场景下,非公平性模式下,新加入的消费者线程有可能在等待队列中的其他消费者线程之前获取到元素。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class UnfairProducerConsumer {
    private static final int QUEUE_CAPACITY = 5;
    private static final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY, false);
    private static final Lock lock = new ReentrantLock(false);
    private static final Condition notFull = lock.newCondition();
    private static final Condition notEmpty = lock.newCondition();

    static class Producer implements Runnable {
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                try {
                    lock.lock();
                    while (queue.size() == QUEUE_CAPACITY) {
                        notFull.await();
                    }
                    queue.put(i);
                    System.out.println("Produced: " + i);
                    notEmpty.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    static class Consumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    lock.lock();
                    while (queue.isEmpty()) {
                        notEmpty.await();
                    }
                    int item = queue.take();
                    System.out.println("Consumed: " + item);
                    notFull.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread producerThread = new Thread(new Producer());
        Thread consumer1Thread = new Thread(new Consumer());
        Thread consumer2Thread = new Thread(new Consumer());

        producerThread.start();
        consumer1Thread.start();
        consumer2Thread.start();
    }
}