zoukankan      html  css  js  c++  java
  • Android学习笔记:多个AsyncTask实例的并发问题

    AsyncTask是Android给开发者提供的一个简单轻量级的多线程类,通过它我们可以很容易新建一个线程让在后台做一些耗时的操作(如IO操作、网络访问等),并在这个过程中更新UI。之所以说它轻量级,是因为不需要直接使用Handler、Thread等知识,使用起来比较简单,但也失去了一些灵活性,对于一些复杂的场景处理起来不方便。

    如果一个APP进程中同时只创建和运行一个AsyncTask实例,则不会有任何问题。但如果在一个进程中如果有多个AsyncTask任务同时在执行,问题就比较复杂了。下面我们通过例子来看(我们例子是在Android 4中运行的)。

    一、测试1(默认多个Task是串行执行的)

    1、创建一个默认的app工程

    2、创建一个类继承AsyncTask,代码如下

    package com.example.asynctaskdemo;
    
    import android.os.AsyncTask;
    
    public class MyAsyncTask extends AsyncTask<String, Void, String>{
    	
    	private String name;
    	
    	public MyAsyncTask(String name){
    		this.name = name;
    	}
    
    	@Override
    	protected String doInBackground(String... params) {
    		System.out.println(name+" is run "+System.currentTimeMillis()+" thread id "+Thread.currentThread().getId());
    		try {
    			Thread.sleep(1000*10);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    
    }
    

    3、在Activity的onCreate方法中使用该Task,代码如下

    @Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		
    		for(int i=1;i<5;i++){
    			MyAsyncTask task = new MyAsyncTask("task"+i);
    			task.execute(new String[0]);
    		}
    	}
    

      我们在调试窗口,观察MyAsyncTask打印信息的间隔和顺序。发现这创建的4个任务是串行执行的,并不是并发的。

    研究了AsyncTask的实现细节,在创建一个AsyncTask并通过其execute方法启动执行时,AsyncTask并不是创建一个独立的线程去执行。AsyncTask是通过线程池来管理和调度进程中的所有Task的。

    在 Android2.3以前的版本(SDK/API 大于等于10的版本)

    多个AsyncTask任务是并发执行的,也就是说如果启动多个task,则会并发执行。但并发执行的数量取决于AsyncTask内部的线程池限制数量。如果超过了这个限额,新的任务只能等待。

     在Android 3.0及以后版本(SDK/API 大于等于11的版本)

    Google从Android 3.0开始对AsyncTask的调度执行做出了一些变化,对于execute提交的任务,按先后顺序每次只运行一个。也就是说它是按提交的次序,每次只启动一个线程执行一个任务,完成之后再执行第二个任务,也就是相当于只有一个后台线程在执行所提交的任务。上面的例子就验证了这一点。

    二、让AsyncTask并发执行

    因为默认情况下多个task是串行的,那怎么样让并发执行呢?AsyncTask增加了一个新的接口executeOnExecutor,这个接口允许开发者提供自定义的线程池来运行和调度Thread。我们把上面代码改下:

    @Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		for(int i=1;i<11;i++){
    			MyAsyncTask task = new MyAsyncTask("task"+i);
    			//task.execute(new String[0]);
    			task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new String[0]);
    		}
    	}
    

      这里我们使用了executeOnExecutor方法代替了execute方法。并且executeOnExecutor方法的第一个参数是一个预定义的线程池。这时这几个task就可以并发执行了。这时我们观察打印的结果,发现有5个任务并发执行,可以看出有5个不同的线程号,查看AsyncTask的源码,发现并发线程数跟设备的cpu数量是有关的,因此不同的设备上可能看到的结果不完全一致,这点需要注意。只有前面的5个任务执行完后,才会执行后面的,并且通过打印的线程号可以看出,后面执行的任务是重用原来的线程,并没有创建新的线程,这就是线程池的作用。

    我们再来改下代码,使用Android提供的另外一个预定义的线程池。代码如下:

    @Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		for(int i=1;i<11;i++){
    			MyAsyncTask task = new MyAsyncTask("task"+i);
    			//task.execute(new String[0]);
    			task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, new String[0]);
    		}
    	}
    

    观察打印信息我们可以发现,这和调用execute方法一样,每个任务都是串行执行的。并且这个过程中最多创建了5个新的线程。

    三、自定义线程池

    我们可以利用java.util.concurrent.Executors中的各种静态方法创建供AsyncTask执行的线程池 ,可以指定线程的数量和调度的方式。其方法很多,我们这里介绍其中两种较为常用的。

    1、让每个AsyncTask任务都单独起一个线程执行,也就是说所有的都是并发的。代码如:

    @Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
    		for(int i=1;i<11;i++){
    			MyAsyncTask task = new MyAsyncTask("task"+i);
    			task.executeOnExecutor(newCachedThreadPool, new String[0]);
    		}
    	}
    

      通过观察打印可以看出,这多个任务都是并发执行的。

    2、创建指定线程数量的线程池,并发数上限就是指定的线程数。但新任务产生,没有空闲的线程,就只能等待。代码如:

    @Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
    		for(int i=1;i<11;i++){
    			MyAsyncTask task = new MyAsyncTask("task"+i);
    			task.executeOnExecutor(newFixedThreadPool, new String[0]);
    		}
    	}
    

      通过观察可以看出,有3个线程在并发执行。

    总结下,从上面的例子中可以看出,如果一个进程中存在多个TASK需要并发执行的情况,那就需要用到AsyncTask一些更深的知识,需要考虑的问题更多。

  • 相关阅读:
    How to install cacti With Nginx
    RPM方式安装MySQL5.6
    mysql高可用方案比较
    甲骨文推出MySQL Fabric,简化MySQL的高可用性与可扩展性
    Atitit.数据库表的物理存储结构原理与架构设计与实践
    Atitit.数据库表的物理存储结构原理与架构设计与实践
    Atitit.java jna 调用c++ dll 的总结
    Atitit.java jna 调用c++ dll 的总结
    Atitit.一些公司的开源项目 重大知名开源项目attilax总结
    Atitit.一些公司的开源项目 重大知名开源项目attilax总结
  • 原文地址:https://www.cnblogs.com/51kata/p/4122655.html
Copyright © 2011-2022 走看看