无论如何在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队列 */上面是一个 ArrayBlockingQueue队列的简单使用,我们应该注意的是在创建 ArrayBlockingQueue对象的时候指定了其大小为10,在初始化(存入Runnable对象的时候)时调用了其add()方法,并且在取出时调用了take()方法,其实BlockingQueue提供了多种方法存入和取出数据。下面详细介绍下这些方法:
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();
}
}
}
(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();
}