android asynctask 多线程,android asyncTask多线程优化

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

1.线程

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

1.1进程

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

特征:

独立性:进程是系统中独立存在的实体,可以拥有自己独立的资源,每个进程都有自己私有的地址空间(独立的代码和数据空间)。但是进程间的切换会有较大的开销。

动态性:进程是一个正在系统中动态指令集合。并在进程中加入了时间概念。进程具有自己的生命周期和各种不同的状态。

并发性:多个进程可以在单个处理器上并发执行,多个线程之间不会互相影响。并行指同一时刻,有多条指令在多个处理器上同时执行。并发指在同一时刻,只能有一条指令执行,但多个进程,使得在宏观上具有多个进程同时执行的效果。

1.2线程

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

一个线程可以拥有自己的堆,栈,自己的程序计算器和自己的局部变量,但不再拥有系统资源,它与父进程的其他线程共享该进程所有的全部资源。

多个进程共享父进程全部资源,因此我们必须确保一个进程不会妨碍同一进程的其他线程。

线程是独立运行的,它并不知道进程中是否还有其他线程的存在。线程的运行是抢占式。当前运行的进程在任何时候都可能被挂起,以便另一个线程可以运行。

一个线程可以创建和撤销另一个线程,同一个进程(Process)的多个线程(Thread)之间可以并发执行。

2.Thread和Runnable

2.1 Thread创建和启动

1.定义子Thread并重写run()。

2.创建线程对象

3.线程对象用start方法启动线程

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 线程的生命周期

新建(new)

就绪(runnable)

运行(running)

阻塞(blocked)

死亡(dead)

dcbef0419efd

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”执行完。

dcbef0419efd

执行结果

2.6 后台程序(Daemon Thread)

指在后台运行的 线程,任务是为其他的线程提供服务。 JVM的垃圾回收线程就是典型的后台线程。

特征:如果所有的前台线程都死亡,那么后台线程会自动死亡。当整个虚拟机中只剩下后台线程时,程序就没有继续运行的必要了,所以虚拟机也就退出了。

设置指定线程为后台线程: 调用Thread对象的setDaemon()方法

前台线程创建的子线程默认是前台线程,后台线程创建的子线程默认是后台线程

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);

}

}

}

dcbef0419efd

执行结果

上面代码在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生产者和消费者

死锁出现的原因

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

1.互斥条件:线程要求对所分配的资源进行排他性控制,即在一段时间内某 资源仅为一个进程所占有.此时若有其他进程请求该资源.则请求进程只能等待.

2.不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的线程自己来释放(只能是主动释放).

3.请求和保持条件:线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求线程被阻塞,但对自己已获得的资源保持不放.

4.循环等待条件:存在一种线程资源的循环等待链,链中每一个线程已获得的资源同时被链中下一个线程所请求。

// 产品

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的子类,可以在指定延迟后执行线程任务。

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

调用Executors类的静态工厂方法创建一个ExecutorService对象,该对象代表一个线程池。

创建Runnable或Callable实现类的实例,作为线程任务

调用ExecutorService对象的submit方法来提交Runnable或Callable任务

当不想再提交任何任务时调用ExecutorService对象的shutdown方法来关闭线程池

public static void main(String[] args) {

Task work = new Task();

FutureTask future = new FutureTask(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{

//返回异步任务的执行结果

@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中调用流程

dcbef0419efd

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 sPoolWorkQueue =

new LinkedBlockingQueue(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();

}

}

}

}

dcbef0419efd

问题

会出现这问题:这个问题产生的原因是默认情况任务队列只分配了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 {

int i = 0;

@Override

protected Void doInBackground(Void... params) {

while (!isCancelled()) {

// Log.d("jason", String.valueOf(i++));

SystemClock.sleep(1000);

}

return null;

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值