多线程(3):队列 BlockingQueue

简介

在线程池中会经常使用BlockingQueue,BlockingQueue是一种阻塞队列,阻塞队列的特性:我在放的时候别人不能放,我在取的时候别人不能取,满的时候就不能再添加,等待有人取走,才能放

public interface BlockingQueue<E> extends Queue<E> {
    // 添加成功返回true,否则抛出异常
    boolean add(E e);
    // 添加成功返回true,失败返回false
    boolean offer(E e);
    boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;
    // 添加时如果没有空间则调用此方法的线程来阻断知道队列有空间再继续
    void put(E e) throws InterruptedException;
    // 取走队列里的首位对象,如果不能立即取出,阻断进入等待状态直到队列有新的对象加入为止
    E take() throws InterruptedException;
    // 取走首位对象,若不能立即取出,可以等unit时间,取不到返回null
    E poll(long timeout, TimeUnit unit) throws InterruptedException;
    boolean remove(Object o);
    // 队列剩余容量
    int remainingCapacity();
    // 是否包含对象
    public boolean contains(Object o);

    // 移除次队列中所有可用的元素,并将它们添加到指定的集合中
    int drainTo(Collection<? super E> c);
    // 指定最大移动元素的个数
    int drainTo(Collection<? super E> c, int maxElements);
}

BlockingQueue

  • ArrayBlockingQueue: 一个由数组支持的有界阻塞队里,规定大小的BlockingQueue,其构造函数必须带一个int参数来指明大小,其所含的对象是以先入先出的顺序排序的
  • LinkedBlockingQueue:基于链表的阻塞队列,大小不定的BlockingQueue,可以指定长度,不指定默认为Interger.MAX_VALUE,也是先入先出的顺序排序的,主要用到put和take,put方法在队列满的时候会阻塞知道有队列成员被消费,take方法在队列空的时候会阻塞,知道有新的队列成员被放入
  • PriorityBlockingQueue:带有优先级的BlockingQueue
  • DelayQueue: 一定时间之后才可以take的BlockingQueue
  • SynchronousQueue:直接传递的BlockingQueue,用于执行Producer角色到Consumer角色的直接传递。

  • ConcurrentLinkedQueue: 元素个数没有最大限制的线程安全队列,该类不是BlockingQueue的子类, 内部数据结构是分开的,线程之间互不影响,所以也就无需执行互斥处理。根据线程情况的不同,有时程序的性能也会有所提高。


示例一
客户端(生产者)不停的向集合中添加元素,服务器端(消费端)不停的从集合中取出元素

public class Request {
    private final String name;


    public Request(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Request{" +
                "name='" + name + '\'' +
                '}';
    }
}

public class RequestQueue {
    // LinkedList也是一种Queue
    private final Queue<Request> queue = new LinkedList<>();

    public synchronized Request getRequest() {
        // 判断队列的头元素是否有值,如果没有返回null,即队列时空的
        // 为啥用while而不用if ? 当等待结束了再次检查队列中是否有元素,这样更加安全
        while (queue.peek() == null) {
            try { wait(); } catch (InterruptedException e) { }
        }

        return queue.remove();
    }

    public synchronized void putRequest(Request request) {
        // 向队列末尾添加元素
        queue.offer(request);
        notifyAll();
    }
}

public class ClientThread extends Thread {
    private final Random random;
    private final RequestQueue requestQueue;

    public ClientThread(RequestQueue requestQueue, String name, long seed) {
        super(name);
        this.requestQueue = requestQueue;
        this.random = new Random(seed);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            Request request = new Request("No." + i);
            System.out.println(Thread.currentThread().getName() + "\trequest\t" + request);
            requestQueue.putRequest(request);

            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ServerThread extends Thread {
    private final Random random;
    private final RequestQueue requestQueue;

    public ServerThread(RequestQueue requestQueue, String name, long seed) {
        super(name);
        this.requestQueue = requestQueue;
        this.random = new Random(seed);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            Request request = requestQueue.getRequest();
            System.out.println(Thread.currentThread().getName() + "\thandles\t" + request);

            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        RequestQueue requestQueue = new RequestQueue();
        new ClientThread(requestQueue, "Alice", 3141592L).start();
        new ServerThread(requestQueue, "Bobby", 6535897L).start();
    }
}

这里写图片描述

上面示例其实就是LinkedBlockingQueue的实现,Java已经提供了这样的功能:java.util.concurrent.LinkedBlockingQueue

修改RequestQueue,其他类和示例一保持不变

public class RequestQueue {
    private final BlockingQueue<Request> queue = new LinkedBlockingQueue<>();

    public Request getRequest() {
        Request request = null;
        try {
            // 取出队列首元素,当队列为空时,就会wait
            // take和put方法已经考虑互斥处理,所以两个方法都不需要synchronized
            request = queue.take();
        } catch (InterruptedException e) {
        }

        return request;
    }

    public void putRequest(Request request) {
        try {
            // 向队列末尾添加元素
            queue.put(request);
        } catch (InterruptedException e) {

        }
    }
}

package java.util.concurrent;

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

        public E take() throws InterruptedException {
            E x;
            int c = -1;
            final AtomicInteger count = this.count;
            // 加锁
            final ReentrantLock takeLock = this.takeLock;
            takeLock.lockInterruptibly();
            try {
                while (count.get() == 0) {
                    notEmpty.await();
                }
                x = dequeue();
                c = count.getAndDecrement();
                if (c > 1)
                    notEmpty.signal();
            } finally {
                takeLock.unlock();
            }
            if (c == capacity)
                signalNotFull();
            return x;
    }

    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        // Note: convention in all put/take/etc is to preset local var
        // holding count negative to indicate failure unless set.
        int c = -1;
        Node<E> node = new Node<E>(e);
        // 加锁
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from capacity. Similarly
             * for all other uses of count in other wait guards.
             */
            while (count.get() == capacity) {
                notFull.await();
            }
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }
}        

示例二

Producer 生产者

public class Producer implements Runnable {
    BlockingQueue<String> queue;

    public Producer(BlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            String temp = "Producer make a thing:" + Thread.currentThread().getName();
            System.out.println("Producer\t" + new Date() + "\t" + Thread.currentThread().getName() + "\t生产中ing...");
            queue.put(temp);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Consumer 消费者

public class Consumer implements Runnable {
    BlockingQueue<String> queue;

    public Consumer(BlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(500);

            // 如果队列为空会阻塞队列
            String value = queue.take();
            System.out.println("Consumer" + "\t" + new Date() + "\t" + Thread.currentThread().getName() + "\ttake\t" + value);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class BlockingQueueTest {
    public static void main(String[] args) {
        BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);
        Producer producer = new Producer(queue);
        Consumer consumer = new Consumer(queue);

        for (int i = 0; i < 10; i++) {
            new Thread(producer, "Producer" + (i+1)).start();
        }

        for (int i = 0; i < 5; i++) {
            new Thread(consumer, "Consumer" + (i+1)).start();
        }
    }
}

这里写图片描述


并发队列


ConcurrentLinkedQueue是一个适用于高并发场景下的队列,通过无锁方式,实现高并发状态下的高性能,通常ConcurrentLinkedQueue性能要好于BlockingQueue,

它是一个基于链接节点的无界线程安全队列,遵守先进先出,头是最先加入的,尾是最近加入的,不允许null值。

public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
        implements Queue<E>, java.io.Serializable {
     // 将元素添加到队列末尾   
     public boolean offer(E e);

     // 取出队列的元素,size减掉1
     public E poll();

     // 如果队列中存在元素,返回头元素,如果为空,返回null
     // 该方法并不移除元素
     public E peek();

     // 队列长度
     public int size();   

     // 移除队列的第一个元素,并返回该元素,如果队列为空,则抛异常NoSuchElementException
     public E remove();    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值