无论如何在Android中都无法避免多线程和并发的操作,并发有可能是app用户请求服务器对服务器来说的并发也有可能是app本身存在多线程并发,今天我们就先从Java基础知识了来学习多线程和并发的操作。
关于线程Thread和Runnable的基本使用就不多说,我们先介绍下控制线程同步的几种方法:
(1)使用synchronized修饰方法、代码块,方法中的变量。
(2)使用volatile修饰成员变量。
(3)使用阻塞队列---BlockingQueue实现同步。
本部分就介绍BlockingQueue的使用。
首先BoockingQueue是一个接口,它有四个实现子类,分别是:
(1)ArrayBlockingQueue:规定大小的BlockingQueue,在创建时,构造函数必须制定int类型的大小,它遵循FIFO的原则。
(2)LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的。
(3)PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序.
(4)SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的.
下面先给出一个ArrayBlockingQueue的使用实例:
/** 创建10个Runnable的Blocking队列 */ private static BlockingQueue<Runnable> mArrayBlockQueue = new ArrayBlockingQueue<Runnable>( 10); /** * 一个测试用到的线程 * * @author Administrator * */ private static class BlockThread implements Runnable { String name; public BlockThread(String name) { this.name = name; } public void run() { System.out.println("--->" + name); } } /** * 初始化mArrayBlockQueue队列 */ private static void initArrayBlockQueue() { for (int i = 0; i < 10; i++) { BlockThread block = new BlockThread("张三" + i); boolean boo = mArrayBlockQueue.add(block); System.out.println(boo); } } //最后在main()方法中使用队列,遍历取出所有的Runnable public static void main(String[] args) { initArrayBlockQueue(); System.out.println("线程大小:" + mArrayBlockQueue.size()); // 遍历取出队列中的Runnable for (int i = 0; i < mArrayBlockQueue.size(); i++) { try { mArrayBlockQueue.take().run(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }上面是一个 ArrayBlockingQueue队列的简单使用,我们应该注意的是在创建 ArrayBlockingQueue对象的时候指定了其大小为10,在初始化(存入Runnable对象的时候)时调用了其add()方法,并且在取出时调用了take()方法,其实BlockingQueue提供了多种方法存入和取出数据。下面详细介绍下这些方法:
(1)add(object):添加一个对象到BlockingQueue,如果添加成功就返回true,如果添加不成功(比如添加的内容超过其容量)则会直接抛出异常导致程序崩溃。
(2)offer(object):添加一个对象到BlockingQueue,如果添加成功就返回false,如果添加失败就返回false,无论如何也不会抛出异常,即使添加失败程序也运行正常。
(3)take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止。
(4)peek():从BlockingQueue取出一个值,但是不删除,因此每次获取都是第一个元素。
(5)poll():从BlockingQueue取出一个值,同时删除该元素。
实际上,查看源码我们会发现,add()方法实际上是调用了offer()方法,而offer()和put()都最终都调用了insert()方法,然后将对象保存在一个Object数组中。
BlockingQueue是一个接口,他有四个实现类,下面分别介绍:
1) ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入 先出)顺序排序的.
2) LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的
3) PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序.
4) SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的.
说到这里,那么BlockingQueue在什么地方用呢?通常我们用在多线程同步控制上,如果有很多线程需要其同步执行,那么使用BlockingQueue是再适合不过的了。
此外,使用阻塞队列也是实现生产--消费者机制的很好的一个案例,测试代码如下:
private static BlockingQueue<String> as = new ArrayBlockingQueue<String>(20); public static void main(String[] args) { // 先消费,发现没有就会阻塞,知道生产者生产了对象 customer(); // 生产 producter(); } /** * 生产者 */ private static void producter() { new Thread(new Runnable() { @Override public void run() { int i = 0; while (true) { i++; try { // 每隔1秒生产一个对象 Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } as.add("哈哈-->" + i); if (i >= 10) { System.out.println("队列大小为:" + as.size()); break; } } } }).start(); } /** * 消费者 */ private static void customer() { new Thread(new Runnable() { @Override public void run() { try { while (true) { System.out.println(as.take()); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); }