阻塞队列之ArrayBlockingQueue

1、ArrayBlockingQueue特点:

a、底层数据结构是数组,Object[];

b、插入、读取操作都有加锁,线程安全;

c、有界队列,必须指定容量;

d、支持公平锁和非公平锁,构造参数可以指定;

2、常用方法及特点

2.1、构造方法

    //构造1:仅指定容量
    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);//调的还是构造2,默认使用的是非公平锁
    }

    //构造2:指定容量,指定是否使用公平锁
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];//可见其数据结构为数组
        lock = new ReentrantLock(fair);//按参数锁类型构造锁对象
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

    //构造3:指定容量,指定是否使用公平锁定,指定数据集合
    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);//调构造2

        //处理初始数据放入
        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }

2.2、插入元素

a、add(E e)--当队列未满时,可以直接插入,返回true;如果队列满了,则会抛异常,整个过程会加锁、不会阻塞;

    // 其调的父类AbstractQueue的add方法,本质是offer方法
    // 加锁、不阻塞
    // 队列满后再放入会抛异常:IllegalStateException("Queue full")
    public boolean add(E e) {
        return super.add(e);
    }

b、offer(E e)--当队列未满时,可以直接插入,返回true;如果队列满了,不会抛异常,会返回false;整个过程会加锁、不会阻塞;

    // 1、检查参数是否为空;
    // 2、加锁;
    // 3、看队列是否已满,满了则直接返回false,否则执行入队动作,然后返回true
    // 4、finally会释放锁定
    // 注意:add方法本质调的也是该方法
    public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)//判断队列是否已满
                return false;
            else {
                enqueue(e);//入队,会根据putIndex赋值数组,修改数量
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

c、offer(E e, long timeout, TimeUnit unit)--和方法b类似,只是增加了延时再试的逻辑,当队列满后,会等待指定的时间再试,如果此时未满了就插入元素,返回true,否则还是已满,则返回false;

    // 增加了延时判断其他同offer
    public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {

        checkNotNull(e);
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length) {
                if (nanos <= 0)
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            enqueue(e);
            return true;
        } finally {
            lock.unlock();
        }
    }

d、put(E e)--当队列未满时,直接放入无返回,如果队列已满,则会一直阻塞等待;整个过程加锁,会阻塞线程;

    //队列满了就开始阻塞等待,知道有空位,然后再执行入队
    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

2.3、取出元素

a、remove()--对应add,当队列非空时,直接返回首元素且移除首元素,当队列为空时,会抛异常,其本质调用的是poll方法,整个过程会加锁处理,不会阻塞;

    public E remove() {
        E x = poll();//本质调用的是poll
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

b、poll()--对应offer,当队列非空时,直接返回首元素且移除首元素,当队列为空时,会直接返回null,整个过程加锁,但不阻塞;

    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : dequeue();//空时直接返回null
        } finally {
            lock.unlock();
        }
    }

c、poll(long timeout, TimeUnit unit) --对应offer构造2,当队列非空时,直接返回首元素且移除首元素,当队列为空时,会等待指定时间再试,如果队列还是为空,则返回null,否则返回首元素且移除首元素;

    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

3、使用示例

import java.util.Date;
import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 数据结构为数组,是有界队列,需要指定容量,支持公平锁和公平锁,操作加锁线程安全
 * 常用方法,
 * 插入:add、offer、put
 * 取出:remove poll take
 * 
 */
public class ArrayBlockingQueueTest {

	private ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(10, true);
	private ExecutorService threadPool = Executors.newSingleThreadExecutor();

	public ArrayBlockingQueueTest() {
	}

	private void log(String msg) {
		System.out.println(Thread.currentThread().getName() + "-->>" + msg);
	}

	private void console() {
		threadPool.submit(new Runnable() {
			public void run() {
				log("请输入指令:");
				Scanner sc = new Scanner(System.in);
				String readLine = null;
				String tmpValue = null;
				try {
					while (true) {
						readLine = sc.nextLine();
						tmpValue = readLine + "_" + (new Date()).getTime();
						if ("quit".equalsIgnoreCase(readLine)) {
							log("bye");
							break;
						} else if ("add".equalsIgnoreCase(readLine)) {
							blockingQueue.add(tmpValue);// 满了会抛异常
							log(tmpValue);
						} else if ("offer".equalsIgnoreCase(readLine)) {
							boolean isOk = blockingQueue.offer(tmpValue);// 满了会返回false
							log(tmpValue);
							log(isOk + "");
						} else if ("put".equalsIgnoreCase(readLine)) {
							blockingQueue.put(tmpValue);// 满了会阻塞
							log(tmpValue);
						} else if ("remove".equalsIgnoreCase(readLine)) {
							log(blockingQueue.remove());// 空了会抛异常
						} else if ("poll".equalsIgnoreCase(readLine)) {
							log(blockingQueue.poll());// 空了会返回null
						} else if ("take".equalsIgnoreCase(readLine)) {
							log(blockingQueue.take());// 空了会阻塞等待
						}
					}
				} catch (Exception ex) {
					log(ex.getMessage());
				} finally {
					sc.close();
				}
			}
		});
		// threadPool.shutdown();
	}

	public static void main(String[] args) {
		ArrayBlockingQueueTest arrayBlockingQueueTest = new ArrayBlockingQueueTest();
		arrayBlockingQueueTest.console();
	}
}

 

猜你喜欢

转载自blog.csdn.net/tobearc/article/details/87655165