【Android】多线程

本文详细介绍了Android中的多线程技术,包括消息机制的工作原理,从概要到源码分析,阐述了主线程的消息循环以及Handler、Looper和Message的交互。此外,还讨论了Android中实现多线程的方式,如Thread和Runnable,以及线程的wait、sleep、join、yield等方法的使用。最后提到了Callable、Future、FutureTask在线程池中的应用,并简单介绍了线程池的概念和优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android多线程

一、消息机制

1.概要

Android应用启动时会有一个默认的主线程(UI线程),主线程中关联一个消息队列,所有操作都被封装成消息交给主线程处理,将获取消息的操作放在死循环中。

开启子线程执行耗时操作后,发消息(sendMessage(msg)或者post Runnable(callback)/Message——》把Message加入消息队列)给主线程的Handler,Handler从消息队列中取消息,通过handMessage更新UI或者其他处理。

其中,消息队列封装在Looper里,Looper与thread关联,handler与looper关联,即handler与thread建立了关联。handler处理消息用handleCallback/handleMessage。即handler读消息后又处理消息。

 

示例:

class MYHandler extends Handler

{

public void handleMessage(Message msg)

{

//更新UI

}

}

主线程中:

MyHandler mHandler=new MyHandler();

new Thread()

{

public void run()

{

//耗时操作

mHandler.sendEmptyMessage(123);

};

}.start();

 

2.Android源码分析

UI线程的消息循环在ActivityThread.main方法中创建,该函数为Android应用程序的入口。

public static void main(String[] args)

{

…………

//创建消息循环looper

Looper.prepareMainLooper();

ActivityThread thread=new ActivityThread ();

thread.attach(false);

if(sMainThreadHanlder==null)

{

//UI线程的handler

sMainThreadHanlder=thread.getHanlder();

}

……

Looper.loop();//执行消息循环

……

}

 

Handler的默认构造函数:

public Handler()

{

……

mLooper=Looper.myLooper();//获取looper

if(mLooper ==null)

throw new RuntimeException();

mQueue=mLooper.mQueue;//获取消息队列

mCallback=null;

}

Handler的其他函数:

public static Looper myLooper()

{

return sThreadLocal.get();

}

//设置UI线程的Looper

public static void prepareMainLooper()

{

prepare();

setMainLooper(myLooper());

myLooper().mQueue.mQuitAllowed=false;

}

private synchronized static void setMainLooper(Looper looper)

{

mMainLooper=looper;

}

 

//为当前线程设置looper

public static void prepare()

{

if(s.ThreadLocal.get!=null)

{

throw …………

}

sThreadLocal.set(new Looper());

}

 

//执行消息循环

public static void loop()

{

Looper me=myLooper();

if(me ==null)

  throw……

MessageQueue queue=me.mQueue;//获取消息队列

……

//死循环

while(true)

{

//获取消息

Message msg=queue.next();

if(msg!=null)

{

if(msg.target==null) return;

}

//处理消息

msg.target.dispatchMessage(msg);

msg.reclycle();//回收消息

}

//消息处理函数,子类覆写

public void handleMessage(Message msg){}

 

private final void handleCallback(Message msg)

{

msg.callback.run();

}

/**

分发消息

post时callback不为空,用handleCallback()处理,sendMeessage的时候一般不设置callback,用handleMessage()处理。*/

public void dispatchMessage(Messgae msg)

{

if(msg.callback!=null) {handleCallback(msg)}

else 

{

if(mCallback!=null)

if(mCallback.handleMessage(msg)) return;

}

handleMessage(msg);

}

 

public final boolean post(Runnable r)

{

return sendMessageDelayed(getPostMessage(r),0);

}

//将Runnable包装成Message对象,将Runnable对象设置给Message对象的callback字段。

private final Message getPostMessage(Runnable r)

{

Message m=Message.obtain();

m.callback=r;

return m;

}

public final boolean sendMessageDelayed(Message msg,long delayMillis)

{

if(delayMillis<0)

{delayMillis=0;}

return sendMessageAtTime(msg,SystemClocl.uptimeMillis()+delayMillis);

}

//将Message对象插入到消息队列

public boolean sendMessageAtTime(Message msg,long uptimeMillis)

{

boolean sent=false;

MessageQueue queue=mQueue;

if(queue!=null)

{

  msg.target=this;

    sent=queue.enqueueMessage(msg,uptimeMillis);

}…………

}

 

创建handler的时候looper一定不能为空。

所以在子线程中创建handler对象时:

new Thread()

{

Handler handler=null;

public void run()
{

Looper.prepare():

handler=new Handler();

Looper.loop();

}

}.start();

 

 

二、Android中的多线程

1.多线程的实现——Thread和Runnable

启动新线程:

① private void startNewThread()

{

new Thread()

{

public void run(){//耗时操作}

}.start();

}

② private void startNewThread()

{

new Thread(new Runnable()

{

public void run(){//耗时操作}

} ).start();

}

实际上thread也是一个runnable,他实现了runnable接口。thread类中有一个Runnable类型的target字段,代表要被执行在这个子线程中的任务。最终被执行的是 Runnable,thread是对Runnable的包装。当启动一个线程时,如果thread的target不为空,则在子线程中执行这个target的run函数,否则执行thread自身的run函数。

 

2.线程的wait、sleep、join、yield

函数名

作用

wait()

当线程执行到wait()方法时,进入等待池中,失去对象的机锁,其他线程可以访问对象。

可以用notify、notifyAll或者指定睡眠时间来唤醒等待池中的线程。

wait()、notify()、notifyAll()必须放在synchronized 块中,否则会抛出异常

sleep

thread的静态函数,使调用线程进入睡眠状态,不能改变对象的机锁,其他线程无法访问这个对象

join

等待目标线程执行完成后再继续执行,即阻塞当前调用join函数所在的线程等接受线程执行完毕后再继续。

yield

目标线程让出执行权限,进入就绪状态,让其他线程优先执行,但是其他线程未必能优先执行

 

wait、notify、sleep示例:

private static Object sLockObject =new Object();

static void waitAndNotifyAll()

{

Thread thread=new WaitThread();

thread.start();

long startTime=System.currentTimeMillis();

try{

synchronized (sLockObject)

{

SLockObject.wait();

}

}catch(Exception e){}

long timeMS=(System.currentTimeMillis()-startTime);

}

static class WaitThread extends Thread
{

public void run()

{

try

{

synchronized(sLockObject)

{

Thread.sleep(3000);

sLockObject.notifyAll();

}

}catch(Exception e){}

}

}

 

join的示例:

static void joinDemo()

{

Worker worker1=new Worker(“work-1”);

Worker worker2=new Worker(“work-2”);

System.out.println(“启动线程1”);

worker1.start();

try

{

worker1.join();//主线程阻塞到worker1执行完成

System.out.println(“启动线程2”);

worker2.start();

worker2.join();//主线程阻塞到worker2执行完成

}catch(InterruptedException e){……}

System.out.println(“主线程继续执行”);

}

static Class Worker extends Thread{

public Worker(String name){super(name);}

public void run()

{

try

{

Thread.sleep(2000);

}catch(InterruptedException e){……}

System.out.println(“work in”+getName());

 

}

}

执行结果:

启动线程1

work in work-1

启动线程2

work in work-2

主线程继续执行

 

yield示例:

static class YieldThread extends Thread

{

public YieldThread(String name){super(name);}

public synchronized void run()

{

for(int i=0;i<MAX;i++)

{

System.out.println(“%s[%d]---->%d\n”,this.getName(),this.getPriority(),i);

if(i==2)

Thread.yield();

}

}

}

static void yieldDemo()

{

YieldThread t1=new YieldThread (“thread-1”);

YieldThread t2=new YieldThread (“thread-2”);

t1.start();

t2.start();

}

执行结果:

thread-1,优先级为:5 ---->0

thread-1,优先级为:5 ---->1

thread-1,优先级为:5 ---->2

thread-2,优先级为:5 ---->0

thread-2,优先级为:5 ---->1

thread-2,优先级为:5 ---->2

thread-1,优先级为:5 ---->3

thread-1,优先级为:5 ---->4

thread-2,优先级为:5 ---->3

thread-2,优先级为:5 ---->4

 

 

3.Callable、Future、FutureTask

callable\future\futuretask只能运用到线程池中,runnable既然运用在thread中又能运行在线程池中。

① callable

泛型接口,有一个返回值为Vcall()函数。runnablerun()函数不能返回结果。

future

接口,为线程池制定了可管理的任务标准,提供了对runnable或者callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作。

futuretask

future的实现类,实现了runnablefuture<v>两个接口。最终都是执行callable类型的任务。

 

示例:

public class FutureDemo

{

static ExecutorService mExe=Executors.newSingleThreadExecutor();//线程池

public static void main(String[] args)

{

try
{

futureWithRunnable();

futureWithCallable();

futureTask();

}catch……

}

private static void futureWithRunnable()throws InterruptedException,ExecutionException

{

Future<?> result=mExe.submit(new Runnable()

{

public void run()

{

fibc(20);

}

});

System.out.println(“future result from runnable”+result.get());

}

private static void futureWithCallable()……

{

Future<Integer>result2=mExe.submit(new Callable<Integer>()

{

public Integer call()throws Exception{return fibc(20);}

});

System.out.println(“future result from callable”+result2.get());

}

private static void futureTask()……

{

FutureTask<Integer>futureTask=new FutureTask<Integer>

(new Callable<Integer>()

{

public Integer call()throws ……{return fibc(20);}

});

mExe.submit(futureTask);

System.out.println(“future result from futureTask”+futureTask.get());

 

}

//斐波那契数列

private static int fibc(int num)

{

if(num==0) return 0;

if(num==1)return 1;

return fibc(num-1)+fibc(num-2);

}

}

执行结果:

future result from runnable: null

future result from callable: 6765

future result from futureTask:6765

 

4.线程池

线程池会创建多个线程并进行管理,提交给线程的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度,有效地管理、调度线程,避免过多的资源消耗。优点如下:

(1)重用存在的线程,减少对象创建、销毁的开销。

(2)可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。

(3)提供定时执行、定期执行、单线程、并发数控制等功能。

 

线程池实现了ExecutorService接口。利用JDK的Executors工厂类来创建线程池。

 

①启动指定数量的线程——ThreadPoolExecutor

启动指定数量的线程以及将任务添加到一个队列中,并将任务分发给空闲的线程。创建后便进入运行状态,调用了shutdown()进入关闭状态,不再接受新的任务,但是还在执行已经提交了的任务,当所有提交的任务执行完了之后就变成终止状态。

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler),参数详细说明如下表。

 

参数名

作用

corePoolSize

线程池中保存的核心线程数

maximumPoolSize

允许创建的最大线程数。当线程数大于核心线程数小于最大线程数时,

keepAliveTime

终止多余的空闲线程的时间

unit

时间单位,可选值为毫秒、秒、分等

workQueue

任务队列,当线程池达到核心线程数,且所有线程都处于活动状态时将新加的任务放到队列中。

threadFactory

线程工厂

handler

拒绝策略,当线程池与任务队列都满了的时候,对新加任务采取的处理策略

 

workQueue:

(1)ArrayBlockingQueue:基于数组结构的有界数列,先入先出对任务进行排序。如果队列满了还有任务进来,则调用拒绝策略。

(2)LinkedBlockingQueue:基于链表结构的无界队列,忽略拒绝策略、最大线程数等参数。

(3)SynchronousQueue:将任务提交给线程而不是加入到新队列,每个插入的操作必须等到另一个调用移除的操作,如果新任务来了线程池没有任何可用线程的话,则调用拒绝策略。

(4)PriorityBlockingQueue:具有优先级的有界队列。

 

拒绝策略:

(1)AbortPolicy:拒绝任务,抛出RejectedExecutionException异常。默认策略

(2)CallerRunsPolicy:拒绝新任务进入,如果线程池还没关闭,将新任务执行在调用线程中。

(3)DiscardOldestPolicy:如果执行程序还没关闭,则位于队列头部的任务将被删除,然后重试执行程序。

(4)DiscardPolicy:加不进的任务都被抛弃了,同时没有异常抛出。


未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值