当前位置:   article > 正文

Android线程池与接口_android 线程池访问接口

android 线程池访问接口

一、线程池的概念
在引入线程池的技术之前,先考率下面两种网络传输情况:
网络请求通常有两种形式:第一种,请求不是很频繁,而且每次连接后会保持相当一段时间来读数据或者写数据,最后断开,如文件下载,网络流媒体等。另一种形式是请求频繁,但是连接上以后读或写很少量的数据就断开连接。考虑到服务的并发问题,如果每个请求来到以后服务都为它启动一个线程,那么这对服务的资源可能会造成很大的浪费,特别是第二种情况。因为通常情况下,创建和销毁线程是需要一定的耗时的,设这个时间为T1,T1 = 线程创建 + 线程销毁,而连接后读/写服务的时间为T2,T2 = 线程运行,当T1>>T2时,我们就应当用线程池机制,使得服务对于第二种请求方式也能在较低的功耗下完成。

单一的new Thread的弊端如下:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比 new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。

线程池,首先在服务启动的时候,我们可以启动好几个线程,并用一个容器(如线程池)来管理这些线程。当请求到来时,可以从池中去一个线程出来,执行任务(通常是对请求的响应),当任务结束后,再将这个线程放入池中备用;如果请求到来而池中没有空闲的线程,该请求需要排队等候。最后,当服务关闭时销毁该池即可。

二、线程池的结构
线程池中通常由这样几个概念(接口)组成:
线程池(Thread pool ),池是一个容器,容器中有很多个执行器,每一个执行器是一个线程。当然,这个容器的实现,可以是链表,可以是数组等等,不需要关心,需要关心的是,池必须提供一个可以从中取出执行器 的方法,可能还需要一个池中现有活动线程数方法,销毁池的方法等。
执行器(Executor ),每个执行器是一个线程,每个执行器可以执行一个任务 ,任务是做什么,此时还不很明确,它需要提供任务的setter/getter方法,并且作为一个线程,他可以独立运行,执行器执行完自身后,需要将自身放入池中。
任务(Task ),任务是每个线程具体要做的事,如资源下载,播放flash片段,打印一段文字到控制台等等,它本身不能执行,而需要将自身交给执行器。

三、Java中四种线程池的使用
Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。


四、接口的引入(概念比较难理解,结合下面例子)
整个池的机制和结构就是这样,当然,需要一个调度者(scheduler)来协调主线程和池的关系。结构,或者接口的目的是为了让我们从细节中解脱出来,从一个比较抽象的层次来描述系统,这样的好处是简单,而且设计出来的框架比较通用,可以适应很多相近相似的情况。由于Task具体干什么我们不知道,所以它几乎可以干任何适应于上边总结的网络连接的第二种情况(T1>>T2)。

五、代码实例

(1)Handler+ExecutorService(线程池)+MessageQueue模式:

  1. package ghj1976.AndroidTest;
  2. import java.io.IOException;
  3. import java.net.URL;
  4. import java.util.concurrent.ExecutorService;
  5. import java.util.concurrent.Executors;
  6. import android.app.Activity;
  7. import android.graphics.drawable.Drawable;
  8. import android.os.Bundle;
  9. import android.os.Handler;
  10. import android.os.Message;
  11. import android.os.SystemClock;
  12. import android.util.Log;
  13. import android.widget.ImageView;
  14. public class MainActivity extends Activity {
  15. @Override
  16. public void onCreate(Bundle savedInstanceState) {
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.main);
  19. loadImage3("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
  20. loadImage3("http://www.chinatelecom.com.cn/images/logo_new.gif",
  21. R.id.imageView2);
  22. loadImage3("http://cache.soso.com/30d/img/web/logo.gif",
  23. R.id.imageView3);
  24. loadImage3("http://csdnimg.cn/www/images/csdnindex_logo.gif",
  25. R.id.imageView4);
  26. loadImage3("http://images.cnblogs.com/logo_small.gif",
  27. R.id.imageView5);
  28. }
  29. private Handler handler = new Handler();
  30. private ExecutorService executorService = Executors.newFixedThreadPool(5);
  31. // 引入线程池来管理多线程
  32. private void loadImage3(final String url, final int id) {
  33. executorService.execute(new Runnable() {
  34. public void run() {
  35. try {
  36. final Drawable drawable = Drawable.createFromStream(
  37. new URL(url).openStream(), "image.png");
  38. // 模拟网络延时
  39. SystemClock.sleep(2000);
  40. handler.post(new Runnable() {
  41. public void run() {
  42. ((ImageView) MainActivity.this.findViewById(id))
  43. .setImageDrawable(drawable);
  44. }
  45. });
  46. } catch (Exception e) {
  47. throw new RuntimeException(e);
  48. }
  49. }
  50. });
  51. }
  52. }

 

(2)Handler+ExecutorService(线程池)+MessageQueue+缓存模式
 下面比起前一个做了几个改造:
 •把整个代码封装在一个类中
 •为了避免出现同时多次下载同一幅图的问题,使用了本地缓存

  1. package ghj1976.AndroidTest;
  2. import java.lang.ref.SoftReference;
  3. import java.net.URL;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. import java.util.concurrent.ExecutorService;
  7. import java.util.concurrent.Executors;
  8. import android.graphics.drawable.Drawable;
  9. import android.os.Handler;
  10. import android.os.SystemClock;
  11. public class AsyncImageLoader3 {
  12. // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)
  13. public Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
  14. private ExecutorService executorService = Executors.newFixedThreadPool(5); // 固定五个线程来执行任务
  15. private final Handler handler = new Handler();
  16. /**
  17. *
  18. * @param imageUrl
  19. * 图像url地址
  20. * @param callback
  21. * 回调接口
  22. * <a href="\"http://www.eoeandroid.com/home.php?mod=space&uid=7300\"" target="\"_blank\"">@return</a> 返回内存中缓存的图像,第一次加载返回null
  23. */
  24. public Drawable loadDrawable(final String imageUrl,
  25. final ImageCallback callback) {
  26. // 如果缓存过就从缓存中取出数据
  27. if (imageCache.containsKey(imageUrl)) {
  28. SoftReference<Drawable> softReference = imageCache.get(imageUrl);
  29. if (softReference.get() != null) {
  30. return softReference.get();
  31. }
  32. }
  33. // 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中
  34. executorService.execute(new Runnable() {
  35. public void run() {
  36. try {
  37. final Drawable drawable = loadImageFromUrl(imageUrl);
  38. imageCache.put(imageUrl, new SoftReference<Drawable>(
  39. drawable));
  40. handler.post(new Runnable() {
  41. public void run() {
  42. callback.imageLoaded(drawable);
  43. }
  44. });
  45. } catch (Exception e) {
  46. throw new RuntimeException(e);
  47. }
  48. }
  49. });
  50. return null;
  51. }
  52. // 从网络上取数据方法
  53. protected Drawable loadImageFromUrl(String imageUrl) {
  54. try {
  55. // 测试时,模拟网络延时,实际时这行代码不能有
  56. SystemClock.sleep(2000);
  57. return Drawable.createFromStream(new URL(imageUrl).openStream(),
  58. "image.png");
  59. } catch (Exception e) {
  60. throw new RuntimeException(e);
  61. }
  62. }
  63. // 对外界开放的回调接口
  64. public interface ImageCallback {
  65. // 注意 此方法是用来设置目标对象的图像资源
  66. public void imageLoaded(Drawable imageDrawable);
  67. }
  68. }


调用者Activity代码:

  1. package ghj1976.AndroidTest;
  2. import android.app.Activity;
  3. import android.graphics.drawable.Drawable;
  4. import android.os.Bundle;
  5. import android.widget.ImageView;
  6. public class MainActivity extends Activity {
  7. @Override
  8. public void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.main);
  11. loadImage4("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
  12. loadImage4("http://www.chinatelecom.com.cn/images/logo_new.gif",
  13. R.id.imageView2);
  14. loadImage4("http://cache.soso.com/30d/img/web/logo.gif",
  15. R.id.imageView3);
  16. loadImage4("http://csdnimg.cn/www/images/csdnindex_logo.gif",
  17. R.id.imageView4);
  18. loadImage4("http://images.cnblogs.com/logo_small.gif",
  19. R.id.imageView5);
  20. }
  21. private AsyncImageLoader3 asyncImageLoader3 = new AsyncImageLoader3();
  22. // 引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程
  23. private void loadImage4(final String url, final int id) {
  24. // 如果缓存过就会从缓存中取出图像,ImageCallback接口中方法也不会被执行
  25. Drawable cacheImage = asyncImageLoader3.loadDrawable(url,
  26. new AsyncImageLoader3.ImageCallback() {
  27. // 请参见实现:如果第一次加载url时下面方法会执行
  28. public void imageLoaded(Drawable imageDrawable) {
  29. ((ImageView) findViewById(id))
  30. .setImageDrawable(imageDrawable);
  31. }
  32. });
  33. if (cacheImage != null) {
  34. ((ImageView) findViewById(id)).setImageDrawable(cacheImage);
  35. }
  36. }
  37. }

 

注,对内部类public interface ImageCallback{}的说明:

这个接口里方法可以接受所有实现这个接口的类的实例,这样就不用关心是哪个类去实现了它。通过接口实现了向上转型,即一个接口可以有多个实现,消去对类型的耦合性

 

(最后补充一点面试常常问到的,Java接口与抽象类的区别:

 

简单来说,接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,
而抽象类是可以有私有方法或私有变量的。

 

另外,
实现接口的一定要实现接口里定义的所有方法,
而实现抽象类可以有选择地重写需要用到的方法。
一般的应用里,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现。
还有,接口可以实现多重继承,而一个类只能继承一个超类,但可以通过继承多个接口实现多重继承,接口还有标识(里面没有任何方法,如Remote接口)和数据共享(里面的变量全是常量)的作用。)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/240453
推荐阅读
相关标签
  

闽ICP备14008679号