Android性能优化

android asyncTask多线程优化

2017-10-24  本文已影响0人  田间小鹿

AsyncTask的线程优化,我们先了解线程和它在java中的怎么使用的。然后分析android中的实现方法。在模拟实验存在的问题。给出解决方法。

1.线程

单线程只有一个顺序执行流,多线程则可以包括多个顺序执行,多个顺序流之间互不干扰。

1.1进程

进程是处于运行过程中的程序,并具有一定独立功能,是系统进行资源分配和调度的一个独立单位。

特征:

1.2线程

线程也被称作轻量级进程。线程是进程的执行单元。就像进程在操作系统中的地位一样,线程在程序中是独立的,并发的执行流。当进程被初始化之后,主线程就被创建了。通常一个程序只有一个主进程,但我们也可以在该进程内创建多条顺序执行流,这些顺序执行流就是Thread,每条Thread也是互相独立的。

2.Thread和Runnable

2.1 Thread创建和启动

 static class FirstThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(this.getName() + " " + i);
            }
        }
    }

2.2 实现Runnable接口创建线程类

1.定义实现Runnable接口的类,重写run方法

public class SecondThread implements Runnable

2.创建Runnable实现类的对象,并以此作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象。

 SecondThread st = new SecondThread();

3.调用线程对象的start方法来启动该线程

 new Thread(st,"TXB").start();

2.3 Thread和Runnable比较

优缺点 继承Thread 实现Runnable
优点 简单,直接使用this.getName()来获取当前线程(因为本身是一个线程类对象) 1.只是实现了Runnable接口,还可以继承其他类2.多个线程共享一个target对象,非常适合多个线程来处理同一份资源的情况
缺点 因为线程类已经继承了Thread,所以不能再继承其他父类了 略微复杂,要使用Thread.currentThread()来获取当前线程

2.4 线程的生命周期

Thread生命周期

==抢占式调度策略==

系统会给每个可执行的线程一小段的时间来处理任务;当该时间段使用完,系统就会剥夺该线程所占据的资源,让其他线程获得执行的机会。在选择下一个线程时,系统会考虑线程的优先级。
就绪和运行状态之间的转换通常不受程序控制,而是由系统线程调度所导致。

2.5 join

当在某个程序执行流中A调用其他线程的join方法,A(调用join方法的那个线程)将被阻塞,知道join方法加入的join线程完成为止。

public class Join {
    
    public static class JoinThread implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
        
    }
    
    public static void main(String[] args) {
        JoinThread jt = new JoinThread();
        new Thread(jt,"TXB").start();

        for(int i=0;i<100;i++){
            System.out.println("i:" + i);
            if(i == 20){
                Thread joinThread = new Thread(jt, "joinThread");
                joinThread.start();
                
                try {
                    joinThread.join();
                } catch (InterruptedException e) {
                    System.out.println("e:" + e);
                }
            }
        }
    }
}

上面程序一共有3条线程:

主线程开始之后启动了名为“TXB”的线程,该子线程将会和main线程并发执行

当主线程的循环变量i等于20时,启动了名为“joinThread”的线程,然后这个线程join进了main线程。注意:此时“joinThread”不会和main线程并发执行,而是main线程必须等该线程执行结束后才可以向下执行。在“joinThread”执行时,实际上只有两条子线程(“TXB” 和 “joinThread”)并发执行,而main线程处于等待(阻塞)状态知道“joinThread”执行完。

执行结果

2.6 后台程序(Daemon Thread)

public class DaemonThread {
    
    public static class Daemon implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
        
    }
    
    public static void main(String[] args) {
        
        Daemon d = new Daemon();
        Thread t = new Thread(d, "DaemonThread");

        t.setDaemon(true);

        t.start();

        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
        }

    }
}
执行结果

上面代码在main方法里先将t设置为后台线程,然后启动该线程(==要将某个线程设置为后台线程,必须在该线程启动之前设置,setDaemon(true)必须在start()之前调用,否则会引发IllegalThreadStateException==)。本来该线程和ing该执行到i = 99才会结束,但是实际上它无法运行到99,因为主线程(程序中唯一的前台线程)运行结束后,JVM会自动退出,后台线程也就自动死亡了。

2.7 sleep和yield

sleep方法:Thread类的静态方法,让当前正在执行的线程暂停一段时间,并且进入阻塞状态。当当前线程调用sleep方法进入阻塞状态之后,在它sleep的时间里,它不会获得执行的机会。就算系统中没有其他可运行的程序,处于sleep的线程也不会运行,因此sleep方法常用于暂停程序的运行。

static void sleep(long millis)
 
static void sleep(long millis,int nanos)

yield:和sleep有点类似,也是Thread类的一个静态方法。它也可以让当前正在执行的线程暂停,但不会使线程阻塞,只是将线程转入就绪状态。

sleep yield
sleep暂停当前线程之后,会给其他线程执行机会,并不考虑线程优先级 yield方法暂停当前线程之后,==只有和当前线程优先级相同或者更高的处于就绪状态(runnable)的线程才能有执行的机会==
sleep方法会将线程转入阻塞状态,知道经过了设定的阻塞时间才会转到就绪状态 yield方法不会将线程转入阻塞状态,它只是强制让当前线程从运行状态(runnig)转到就绪状态(runnable)。因此完全有可能某个线程调用yield暂停之后又马上获得CPU资源被执行
sleep方法会抛出InterruptedException异常 不抛异常
sleep方法比yiled方法具有更好的移植性 通常不要依靠yield来控制并发线程的执行

2.9生产者和消费者

死锁出现的原因

产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生.

 // 产品
    static class ProductObject {
        // 线程操作变量可见
        public volatile static String value;
    }

    // 生产者线程
    static class Producer extends Thread {
        Object lock;

        public Producer(Object lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            // 不断生产产品
            while (true) {
                synchronized (lock) { // 互斥锁
                    // 产品还没有被消费,等待
                    if (ProductObject.value != null) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    // 产品已经消费完成,生产新的产品
                    ProductObject.value = "NO:" + System.currentTimeMillis();
                    System.out.println("生产产品:" + ProductObject.value);
                    lock.notify(); // 生产完成,通知消费者消费
                } 
            }
        }
    }

    // 消费者线程
    static class Consumer extends Thread {
        Object lock;

        public Consumer(Object lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    // 没有产品可以消费
                    if (ProductObject.value == null) {
                        // 等待,阻塞
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("消费产品:" + ProductObject.value);
                    ProductObject.value = null;
                    lock.notify(); // 消费完成,通知生产者,继续生产
                }
            
            }
        }
    }
    
    

3. Callabe , Future ,线程池

Callable是Runnable接口的增强版,Callable也提供了一个call()方法作为线程执行体

call()方法可以有返回值

call()方法可以声明抛出异常

Callable问题

Callable接口并不是Runnable接口的子接口,而Thread的构造方法里形参的类型Runnable,所以Callable对象不能直接做为Thread的target;而且call方法不能直接调用,而是作为线程执行体被调用。

为了解决这几个问题:

java提供了Future接口来代替Callable接口的call方法,并为Futrue接口提供一个FutureTast实现类,这个实现了Future接口,也实现了Runnable接口。

线程池

线程池在系统启动时就创建了大量空闲的线程,程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束之后,该线程不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run方法。

Executors工厂类来生产线程池

方法 描述
newCachedThreadPool() 创建一个具有缓存功能的线程池,系统根据需要创建线程,这线程会被缓存在线程池中
newFixedThreadPool(int nThreads) 创建一个可重用的、具有固定线程数的线程池
newSingleThreadExecutor() 创建一个只有单线程的线程池,相当于newFixedThreadPool(int nThreads)传入参数为1
newScheduledThreadPool(int corePoolSize) 创建具有固定线程数的线程池,可以在指定延迟后执行线程任务。corePoolSize指池中所保存的线程数,即使线程是空闲的也被保存在线程池里
newSingleThreadScheduledExecutor() 创建只有固定线程的线程池,可以在指定延迟后执行线程任务。

前三个方法返回一个ExecutorService对象,该对象代表一个线程池,可以执行Runnable或Callable对象所代表的线程。

后两个方法返回一个ScheduledExecutorService,是ExecutorService的子类,可以在指定延迟后执行线程任务。

使用线程池来执行线程任务的步骤:

public static void main(String[] args) {
        Task work = new Task();
        FutureTask<Integer> future = new FutureTask<Integer>(work){
            //异步任务执行完成,回调
            @Override
            protected void done() {
                try {
                    System.out.println("done:"+get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        };
        //线程池(使用了预定义的配置)
        ExecutorService executor = Executors.newCachedThreadPool();
        //executor.submit(future);
        executor.execute(future);
//      new Thread(future,"TXB").start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        //取消异步任务
        //future.cancel(true);
        
        try {
            //阻塞,等待异步任务执行完毕
            System.out.println(future.get()); //获取异步任务的返回值
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    
    //异步任务
    static class Task implements Callable<Integer>{

        //返回异步任务的执行结果
        @Override
        public Integer call() throws Exception {
            int i = 0;
            for (; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + "_"+i);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return i;
        }   
    }

4.android中的AsyncTask

在android中调用流程


AsyncTask流程

模拟android中的AsyncTask

public static void main(String[] args) {
        int CPU_COUNT = Runtime.getRuntime().availableProcessors();  //可用的CPU个数
        int CORE_POOL_SIZE = CPU_COUNT + 1; //5
        int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; //9
        int KEEP_ALIVE = 1;
        
        //任务队列(128)
        final BlockingQueue<Runnable> sPoolWorkQueue =
                new LinkedBlockingQueue<Runnable>(128);
        
        //线程工厂
        ThreadFactory sThreadFactory = new ThreadFactory() {
            private final AtomicInteger mCount = new AtomicInteger(1);

            public Thread newThread(Runnable r) {
                String name = "Thread #" + mCount.getAndIncrement();
                System.out.println(name);
                return new Thread(r, name);
            }
        };
        
        //线程池
        Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
        
        //执行异步任务
        //如果当前线程池中的数量大于corePoolSize,缓冲队列workQueue已满,
        //并且线程池中的数量等于maximumPoolSize,新提交任务由Handler处理。
        //RejectedExecutionException
        for (int i = 0; i < 200; i++) {
            //相当于new AsyncTask().execute();
            THREAD_POOL_EXECUTOR.execute(new MyTask());
        }
    }
    
    static class MyTask implements Runnable{

        @Override
        public void run() {
            //System.out.println(Thread.currentThread().getName());
            while(true){
                try {
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        
    }
问题

会出现这问题:这个问题产生的原因是默认情况任务队列只分配了128,如果我们生存了200个任务,就会出现这个问题。还有一个关键是任务阻塞了,也就是很耗时的时候。

怎么解决了:我们可以进行线程池扩容
Executor THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(25);

在android中调用异步任务时使用new MyTask().executeOnExecutor(exec);

在这有可能出现内存泄露的问题。

产生的原因是如果在子线程中一直处理一些事情,时间比较长,activity在旋转或退出的时候,这个线程还好执行,并保存activity的引用,让其无法释放。

解决方法:

  @Override
    protected void onDestroy() {
        super.onDestroy();
        task.cancel(true); // 取消任务
    }
    
  class MyTask extends AsyncTask<Void, Integer, Void> {
        int i = 0;

        @Override
        protected Void doInBackground(Void... params) {
            while (!isCancelled()) {
                // Log.d("jason", String.valueOf(i++));
                SystemClock.sleep(1000);
            }
            return null;
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读