Galley2中线程池的应用
来源:互联网 发布:格罗姆地狱咆哮知乎 编辑:程序博客网 时间:2024/06/10 16:17
Gallery2是原生android中常用的一个应用,在对图像的批量处理、获取图片缩略图等场景时运用最多的就是线程池了,线程池的代码主要放在了com.android.gallery3d.util包下面,几个文件如下:
Future.java
FutureListener.java
PriorityThreadFactory.java
ThreadPool.java
从Galelry2中线程池的初始化说起:GalleryAppImpl.java(该类继承自Application)中:
@Override public synchronized ThreadPool getThreadPool() { if (mThreadPool == null) { mThreadPool = new ThreadPool(); } return mThreadPool; }可以看到通过该单例方法获取一个ThreadPool对象并返回,那么该线程池具体是如何初始化的呢?继续走到ThreadPool.java中来看......
public ThreadPool(int initPoolSize, int maxPoolSize) { mExecutor = new ThreadPoolExecutor( initPoolSize, maxPoolSize, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("thread-pool", android.os.Process.THREAD_PRIORITY_BACKGROUND)); }从该方法可以看到,里面只是new ThreadPoolExecutor。ThreadPoolExecutor是在java.util.concurrent包下面的(处理java并发),其中参数较多,建议先了解一下该部分基本知识,链接:http://www.infoq.com/cn/articles/java-threadPool/ ,如果文章看完后,应该对其中的每个参数都有所了解。
值得注意的是线程池中线程的创建是通过PriorityThreadFactory.java,可以看到创建的线程命名都是以"thread-pool-" + 数字:
public PriorityThreadFactory(String name, int priority) { mName = name; mPriority = priority; } @Override public Thread newThread(Runnable r) { return new Thread(r, mName + '-' + mNumber.getAndIncrement()) { @Override public void run() { Process.setThreadPriority(mPriority); super.run(); } }; }在做平台开发时,经常会碰到ANR的问题,一般我们通过trace分析各个线程的堆栈信息。如果怀疑是线程池这块出了问题,就可以直接搜索"thread-pool-",来进行定位。
分析完了线程池的初始化,下面就是如何向线程池中提交任务?从ThradPool.java中:
public <T> Future<T> submit(Job<T> job, FutureListener<T> listener) { Worker<T> w = new Worker<T>(job, listener); mExecutor.execute(w); return w; } public <T> Future<T> submit(Job<T> job) { return submit(job, null); }从上面可以看出,如果需要提交任务到线程池,则需要实现Job接口,然后在生成一个Worker对象,一点一点来,先看Job接口都有什么:
// A Job is like a Callable, but it has an addition JobContext parameter. public interface Job<T> { public T run(JobContext jc); }该接口很简单,里面就是一个run方法(是不是很像runnable接口),这里面就是线程真正执行的地方,如果要了解线程怎么执行其run方法,那么还需要去看看Worker这个类,该类涉及内容较多:
private class Worker<T> implements Runnable, Future<T>, JobContext {
该类实现了runnable接口,以及Future和JobContext接口(这两个是什么先不用管),我们先把大致的过程梳理一下:
1、从上面的submit方法中可以看出,真正提交到线程池的是Worker对象,该对象实现了runnable接口,那么当线程池轮到该线程执行时,势必会执行其run方法;
2、我们提交的实现Job接口中的run方法在什么时候执行呢?仔细想想就会明白,它肯定是在Worker对象的run方法中执行的,不信?那么来验证一下:
// This is called by a thread in the thread pool. @Override public void run() { T result = null; // A job is in CPU mode by default. setMode returns false // if the job is cancelled. if (setMode(MODE_CPU)) { try { result = mJob.run(this); } catch (Throwable ex) { Log.w(TAG, "Exception in running a job", ex); } } synchronized(this) { setMode(MODE_NONE); mResult = result; mIsDone = true; notifyAll(); } if (mListener != null) mListener.onFutureDone(this); }从上面代码可以很清楚的看到try catch语句中的mJob.run(this),果然和我们猜测的一样。继续看代码会发现要想执行这个run方法必须setMode这个方法返回true,这个方法究竟是干什么?猜不到,继续向下分析吧......
@Override public boolean setMode(int mode) { // Release old resource ResourceCounter rc = modeToCounter(mMode); if (rc != null) releaseResource(rc); mMode = MODE_NONE; // Acquire new resource rc = modeToCounter(mode); if (rc != null) { if (!acquireResource(rc)) { return false; } mMode = mode; } return true; } private ResourceCounter modeToCounter(int mode) { if (mode == MODE_CPU) { return mCpuCounter; } else if (mode == MODE_NETWORK) { return mNetworkCounter; } else { return null; } }注意其中mMode默认为MODE_NONE,setMode默认传入的参数为MODE_CPU:
ResourceCounter mCpuCounter = new ResourceCounter(2); private static class ResourceCounter { public int value; public ResourceCounter(int v) { value = v; } }
这里要注意mCpuCounter是ThreadPool的属性,是线程间共享的。现在需要搞清楚ResourceCounter的作用,从上面的setMode方法内的第11行看起,看它具体的内容:
private boolean acquireResource(ResourceCounter counter) { while (true) { synchronized (this) { if (mIsCancelled) { mWaitOnResource = null; return false; } mWaitOnResource = counter; } synchronized (counter) { if (counter.value > 0) { counter.value--; break; } else { try { counter.wait(); } catch (InterruptedException ex) { // ignore. } } } } synchronized (this) { mWaitOnResource = null; } return true; }这里进入之后是个死循环,首先判断是否有取消的操作,如果有的话直接返回false。否则,判断counter.value的大小,从上面的setMode中可知,默认MODE_CPU情况下couter.value的值为2。所以首先会将counter.value减1,然后break跳出循环,从而返回true,最终Job的run方法得到执行。
继续分析会发现当线程池中
第二个线程运行到这里会使counter.value变为0,
第三个线程运行到这里时会走到else里面,导致counter进入wait状态,这里要注意处于wait状态是会释放同步锁的。
第四个线程运行到这里同样会处于wait状态。
处于wait状态需要couter.notify()或者notifyAll()方法来唤醒。主要有两个地方来唤醒,第一个:
@Override public synchronized void cancel() { if (mIsCancelled) return; mIsCancelled = true; if (mWaitOnResource != null) { synchronized (mWaitOnResource) { mWaitOnResource.notifyAll(); } } if (mCancelListener != null) { mCancelListener.onCancel(); } }
可以看出如果用户调用了cancel方法,则会调用mWaitOnResource.notifyAll()方法,会唤醒。唤醒后重新执行while循环内,发现最终会返回false,也就意味着取消该任务,其提交的job中的run方法将不会执行。
第二个唤醒的地方:
private void releaseResource(ResourceCounter counter) { synchronized (counter) { counter.value++; counter.notifyAll(); } }
该方法,是在执行中执行setMode方法(第二次进入,此时mMode为MODE_CPU,也即是Worker中run方法的第17行)会将counter.value加1。并且唤醒其它处于wait状态的线程,从而使别的线程能够获取资源,从而得到执行。
整体运行的思路已经走完了,总结一下:
1、线程池中有4个线程,而真正同时并行运行job的run()方法的线程只有2个,主要是通过ResourceCounter来控制(可以通过修改ResourceCounter的value属性来修改);
2、上面还有一些内容没有涉及到,就是FutureListenser、CancelListener等接口,这些从接口的名字就可以知道FutureListener是在job中的run方法运行完后会回调其中的方法,CancelListener是job被取消后会回调其中的方法。只要上面的整个流程清晰后,这些内容都可以结合源代码很容易的理解;
3、调试时可以将断点打在Worker中的run方法中,只要线程池执行到的时候肯定会执行其run方法。
- Galley2中线程池的应用
- JDK1.5中线程池的应用。
- C#中线程池的简单应用
- Spring中线程池的应用
- Java/Android中线程池的应用
- 线程池中CompletionService的应用
- Spring中线程池的应用
- Spring中线程池的应用
- 4中线程池的应用
- Spring中线程池的应用
- C# 中线程的应用
- android中线程的应用
- Android中线程的应用
- 线程池的应用
- iOS编程中线程的应用以及线程简单应用
- Android中线程池的原理和应用
- VC中线程的应用事例
- Java中线程的高级应用
- 常见嵌入式WEB服务器
- js常用工具库XJsTool v1.0发布
- 配置nginx支持PATH_INFO
- android学习之二·简单ListView 添加图片 和多行的Item
- 关于android下无法使用iap继续购买消费型道具的问题
- Galley2中线程池的应用
- 安卓入门之往SD卡存文件
- 2014/12/21
- linux下的内核测试工具——perf使用简介
- Jenkins+maven+git+sonar 系统持续集成&代码单测管理
- Foundation框架第二弹:NSString常用方法
- UVA - 10194 Football (aka Soccer)
- (i++)+(++i)+(i++)之罪恶语句
- android listview gridview 点击无效果