您的位置:首页 > 移动开发 > Android开发

《android开发-从小工到专家》学习记录(七)

2018-02-23 00:27 344 查看
对于Android平台来说,由于资源有限,最常使用的就是通过Executors.newFixedThreadPool(int size)函数来启动固定数量的线程,使用代码如下:

package zs;

import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ABC {
private static void fixedThreadPool(int size)
throws CancellationException,ExecutionException, InterruptedException
{
ExecutorService executorService = Executors.newFixedThreadPool(size);
for(int i = 0;i < 10;i++)
{
Future<Integer> task = executorService.submit(new Callable<Integer>(){
@Override
public Integer call() throws Exception{
System.out.println("执行线程: "+Thread.currentThread().getName());
return add(50);
}
});
System.out.println("第"+i+"次计算,结果:"+task.get());
}
}
private static int add(int num)
{
int sum = 0;
for(int i = 0;i<num;i++)
{
sum = sum + num;
}
return sum;
}
public static void main(String[] args)
{
try{
fixedThreadPool(3);
}catch(Exception e)
{
e.printStackTrace();
}
}
}上述代码启动了一个含有3个线程的线程池,结果如下图:



结果中可以看出,线程池中一共有三个线程交替使用,完成了submit函数提交的十个任务。

有时候我们需要任务尽可能地快被执行,这就需要线程池中的线程足够多。也就是说此时需要使用空间来换时间,线程越多占用的内存消耗自然也就越大,但是,在正常情况下它的并发量也会更大,带来的是执行速度也越快,使用Executors的newCachedThreadPool函数可以实现---来了一个新任务,并且没有空闲线程可用,就马上创建一个线程来执行任务,使用代码如下:
package zs;

import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ABC {
private static void fixedThreadPool(int size)
throws CancellationException,ExecutionException, InterruptedException
{
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i = 0;i < 10;i++)
{
/*Future<Integer> task = executorService.submit(new Callable<Integer>(){
@Override
public Integer call() throws Exception{
System.out.println("执行线程: "+Thread.currentThread().getName());
return add(50);
}
});
System.out.println("第"+i+"次计算,结果:"+task.get());*/
executorService.submit(new Runnable(){
@Override
public void run()
{
System.out.println("执行线程: "+Thread.currentThread().getName()+",结果:"+add(50));
}
});
}
}
private static int add(int num)
{
int sum = 0;
for(int i = 0;i<num;i++)
{
sum = sum + num;
}
return sum;
}
public static void main(String[] args)
{
try{
fixedThreadPool(3);
}catch(Exception e)
{
e.printStackTrace();
}
}
}
注意到不在使用task.get()获得返回值,get方法会阻塞线程,从而使结果不明显,大家可以自己试试看,使用Runnable得到的结果如下:



可以看到该线程池为每个任务都创建了一个线程。

我们还可以使用ScheduledThreadPoolExecutor进行执行定时任务,执行代码可以翻阅网上其他资料或者翻阅此书,我没有敲,就不贴出来了。

在开发的过程中我们会使用到同步集合和同步锁来完成程序:
CopyOnWriteArrayList是一种同步集合的实现,适用于读多写少的条件,写入的方法使用Copy-On-Write优化策略,先把列表中的元素复制一份,然后进行修改,修改完成之后再将新的元素设置给这个列表,使用的是一种延时懒惰策略,这样做的好处是我们可以对列表进行并发地读;
ConcurrentHashMap使用锁分离技术,解决了HashTable容器在竞争激烈的并发环境下效率低下的问题,HashTable效率低下的原因是在高并发环境下,所有访问线程都必须竞争同一把锁,而ConcurrentHashMap中有多把锁,每一把锁用于锁容器其中一部分数据,这样多线程访问不同段的时候,线程间就不会存在竞争;
BlockingQueue是一种生产者消费者的实现,队列满时put函数会将调用线程阻塞,直到队列不满,take函数如果队列为空时,同样将线程阻塞,直到队列中有元素。
下面使用同步锁实现一个BlockingQueue:
package zs;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyArrayBlockingQueue<T> {
//数据数组
private final T[] items;
//锁
private final Lock lock = new ReentrantLock();
//队列满的条件
private Condition notFull = lock.newCondition();
//队列空的调节
private Condition notEmpty = lock.newCondition();
private int head;
private int tail;
//数据个数
private int count;
public MyArrayBlockingQueue(int maxSize)
{
items = (T[]) new Object[maxSize];
}
public MyArrayBlockingQueue()
{
this(10);
}
public void put(T t)
{
lock.lock();
try{
while(count == getCapacity())
{
System.out.println("数据已满,等待");
notFull.await();
}
items[tail] = t;
if(++tail == getCapacity())
{
tail = 0;
}
++count;
notEmpty.signalAll();
}catch(InterruptedException e)
{
e.printStackTrace();
}finally{
lock.unlock();
}
}
public T take()
{
lock.lock();
try{
while(count == 0)
{
System.out.println("暂无数据");
notEmpty.await();
}
T ret = items[head];
if(++head == getCapacity())
{
head = 0;
}
--count;
notFull.signalAll();
return ret;
}catch(InterruptedException e)
{
e.printStackTrace();
}finally{
lock.unlock();
}
return null;
}
public int getCapacity()
{
return items.length;
}
public int size()
{
lock.lock();
try{return count;}
finally{
lock.unlock();
}
}
public static void main(String[] args)
{
MyArrayBlockingQueue<Integer> aQueue
= new MyArrayBlockingQueue<Integer>();
aQueue.put(3);
aQueue.put(24);
for(int i = 0;i<5;i++)
{
System.out.println(aQueue.take());
}
}
}

ReentrantLock类有一个重要的函数,newCondition(),该函数用于获取Lock上的一个条件,Condition与Lock绑定,用于实现线程间的通信,具体使用方法见上述代码,需要注意的是lock必须在finally块中释放,否则如果受保护的代码会弹出异常,锁就永远得不到释放,会出现问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: