Android ArrayBlockingQueue使用

 

 1. ArrayBlockingQueue使用示例

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

//调用两个线程来跑
ArrayBlockedQueueTest blockQueue = new ArrayBlockedQueueTest();
blockQueue.startProducerThread();
blockQueue.startConsumerThread();
package com.blockedqueue;

import android.util.Log;
import java.util.concurrent.ArrayBlockingQueue;

public class ArrayBlockedQueueTest {
    public static final String TAG = "Test.BlockedQueue";
    public static final int SIZE = 2;
    public volatile ArrayBlockingQueue<String> blockedQueue = new ArrayBlockingQueue<>(SIZE, true);
    private int num = 0;

    public ArrayBlockedQueueTest() {

    }

    //生产者线程
    public void startProducerThread() {
        Thread producerThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    feedData();
                }
            }
        });
        producerThread.start();
    }

    //消费者线程
    public void startConsumerThread() {
        Thread consumerThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    getData();
                }
            }
        });
        consumerThread.start();
    }

    public void feedData()  {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if(blockedQueue.size() >= SIZE){
            Log.d(TAG,"feedData size ==  " + blockedQueue.size());
            blockedQueue.remove();
        }
        String str = "String:" + num;
        blockedQueue.add(str);
        Log.d(TAG,"feedData: " + str);
        num++;
    }

    public void getData() {
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String str = blockedQueue.poll(); //這裏應該使用take()阻塞
        if (null  == str) {
            Log.d(TAG,"getData poll null ..." + blockedQueue.size());
            return;
        }
        Log.d(TAG,"getData: " + str);

    }
}


2. 添加和取出方法  


拥有的添加和取出方法如下

方法\处理方式抛出异常返回特殊值一直阻塞超时退出
插入方法add(e)offer(e)put(e)offer(e,time,unit)
移除方法remove()poll()take()poll(time,unit)
检查方法element()peek()不可用不可用
  •  抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出 IllegalStateException("Queue full") 异常。当队列为空时,从队列里获取元素时会抛出 NoSuchElementException 异常 。
  • 返回特殊值:插入方法会返回是否成功,成功则返回 true。移除方法,则是从队列里拿出一个元素,如果没有则返回 null
  •  一直阻塞:当阻塞队列满时,如果生产者线程往队列里 put 元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里 take 元素,队列也会阻塞消费者线程,直到队列可用。
  • 超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。


3. 方法详细使用

 生产者线程慢于消费者

生产者1s入队一次
消费者0.3s出队一次

//生产者
public void feedData()  {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if(blockedQueue.size() >= SIZE){
        Log.d(TAG,"feedData size ==  " + blockedQueue.size());
        blockedQueue.remove();
    }
    String str = "String:" + num;
    blockedQueue.add(str);
    Log.d(TAG,"feedData: " + str);
    num++;
}
//消费者
public void getData() {
    try {
        Thread.sleep(300);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    String str = blockedQueue.poll(); //這裏應該使用take()阻塞
    if (null  == str) {
        Log.d(TAG,"getData poll null ..." + blockedQueue.size());
        return;
    }
    Log.d(TAG,"getData: " + str);
}

结果:消费者会等,poll()出队是返回null

Test.BlockedQueue: getData poll null ...0
Test.BlockedQueue: getData poll null ...0
Test.BlockedQueue: feedData: String:50
Test.BlockedQueue: getData: String:50
Test.BlockedQueue: getData poll null ...0
Test.BlockedQueue: getData poll null ...0
Test.BlockedQueue: feedData: String:51
Test.BlockedQueue: getData: String:51

使用remove时,队列为null,则报NoSuchElementException异常,因此使用这个时应该判断队列的size

String str = blockedQueue.remove();

AndroidRuntime: FATAL EXCEPTION: Thread-3
AndroidRuntime: Process: com.tsp.dtest1129, PID: 6581
AndroidRuntime: java.util.NoSuchElementException
AndroidRuntime:     at java.util.AbstractQueue.remove(AbstractQueue.java:117)
AndroidRuntime:     at com.tsp.blockedqueue.ArrayBlockedQueueTest.getData(ArrayBlockedQueueTest.java:65)
AndroidRuntime:     at com.tsp.blockedqueue.ArrayBlockedQueueTest$2.run(ArrayBlockedQueueTest.java:35)
AndroidRuntime:     at java.lang.Thread.run(Thread.java:919)

正确写法:
if(blockedQueue.size() == 0) {
    return;
}
String str = blockedQueue.remove();

使用take()时,队列是null的时候,会阻塞,等队列不为null

String str = null;
try {
    str = blockedQueue.take();
} catch (InterruptedException e) {
    e.printStackTrace();
}
Log.d(TAG,"getData: " + str);

Test.BlockedQueue: feedData: String:2
Test.BlockedQueue: getData: String:2
Test.BlockedQueue: feedData: String:3
Test.BlockedQueue: getData: String:3
Test.BlockedQueue: feedData: String:4
Test.BlockedQueue: getData: String:4
Test.BlockedQueue: feedData: String:5
Test.BlockedQueue: getData: String:5
Test.BlockedQueue: feedData: String:6
Test.BlockedQueue: getData: String:6


 4. volatile关键字


volatile是处理多线程锁的替代方案,对应有时需要实时的修改共享资源的变量,被volatile修复的变量的值可以立刻被业务方取得最新的值。
 
不过,猛地感觉,nnd,这不是一样么,static是静态的,所以理论上也可以在不同线程去访问,能访问也就是能修改,所以这里老穆在网上搜了搜 相关的资料,把这个知识点在加强下:
 
变量放在主存区上,使用该变量的每个线程,都将从主存区拷贝一份到自己的工作区上进行操作。

volatile, 声明这个字段易变(可能被多个线程使用),Java内存模型负责各个线程的工作区与主存区的该字段的值保持同步,即一致性。

static, 声明这个字段是静态的(可能被多个实例共享),在主存区上该类的所有实例的该字段为同一个变量,即唯一性。

volatile, 声明变量值的一致性;static,声明变量的唯一性。

此外,volatile同步机制不同于synchronized, 前者是内存同步,后者不仅包含内存同步(一致性),且保证线程互斥(互斥性)。
static 只是声明变量在主存上的唯一性,不能保证工作区与主存区变量值的一致性;除非变量的值是不可变的,即再加上final的修饰符,否则static声明的变量,不是线程安全的。

下面摘自Java语言规范(Java Language Specification)的官方解释:

1) If a field is declared static, there exists exactly one incarnation of the field, no matter how many instances (possibly zero) of the class may eventually be created. 

2) A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable。
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,可以为您提供一个简单的Java ArrayBlockingQueue使用例子: ``` import java.util.concurrent.ArrayBlockingQueue; public class ArrayBlockingQueueExample { public static void main(String[] args) { // 创建一个容量为3的数组阻塞队列 ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3); // 添加元素到队列 queue.add("Java"); queue.offer("Python"); try { // 尝试添加一个元素,但队列已满,因此将会被阻塞 queue.put("C++"); } catch (InterruptedException e) { e.printStackTrace(); } // 队列已满,再次添加元素将会抛出异常 try { queue.add("Ruby"); } catch (IllegalStateException e) { System.out.println("队列已满!"); } // 从队列中获取并移除头部元素 System.out.println(queue.poll()); System.out.println(queue.poll()); System.out.println(queue.poll()); // 队列中已没有元素,再次获取元素将返回null System.out.println(queue.poll()); } } ``` 这个例子创建了一个容量为3的ArrayBlockingQueue对象,首先添加了两个元素 Java 和 Python。然后尝试向队列中添加一个元素 C++,但这个队列已满,因此该方法将会被阻塞,直到队列中有空间可用。 接着尝试再次添加一个元素 Ruby,但由于队列已满,该操作将会抛出IllegalStateException异常。 最后,我们从队列中获取并移除了头部的三个元素,然后再次尝试获取一个元素,但由于队列已空,该操作将返回null。 希望这个例子对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值