进程
- 狭义:进程是正在运行的程序的实例
- 广义:进程是一个具有一定独立功能的程序关于数据集合的一次运行活动
进程和线程的区别
- 一个进程至少一个线程
- 进程可以包含多个线程
- 进程在执行过程中拥有独立的内存空间,而线程运行在进程内
多进程的好处
- 更大的内存分配
- 防止进程被杀死
- 独立进程完成任务
单进程如何实现不同控件之间的数据传输
多进程造成的问题
- 静态成员和单例模式完全失效
- 线程同步机制完全失效
- SharedPreferences可靠性下降
- 由于Application多次创建,所以我们不能直接将一些数据保存在Application中
Serializable接口
序列化和反序列化
序列化:将数据结构或对象转换成二进制的过程
反序列化:将在序列化过程中生成的二进制串转换成数据结构或对象的过程
序列化应用场景
- 内存中的对象状态保存到文件或数据库
- 网络上传送对象
- 进程间传递对象
Bundle
应用场景
- 数据的保存和恢复
- 给Fragment传递数据
- 不同线程间通信
- 开启另一个activity的时候
我们都知道Android中ActivityThread是主线程,那么Android中在什么位置判断是否是主线程呢?
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
概述
线程:线程是程序执行流的最小单位,进程是线程的容器,一个进程可以有一个或多个线程,各个线程共享进程的内存.
线程的状态
- New:新创建状态。线程被创建还没有调用start方法,在运行之前还有一些基础工作要做
- Runnable:可运行状态,一旦调用start方法,线程就会处于Runnable状态。一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供的运行时间
- Blocked:阻塞状态。表示线程被锁阻塞,暂时不活动
- Waiting:等待状态,线程暂时不活动,并且不运行任何代码,这消耗最少的资源,直到线程调度器
重新激活它 - Timed waiting:超时等待状态。和等待状态不同的是,它是可以在指定的时间自行返回的。
-
Terminated:终止状态。表示当前线程已经执行完毕。导致线程终止有两种情况:第一种就是run方
法执行完毕正常退出;第二种就是因为一个没有捕获的异常而终止了run方法,导致线程进入终止状态
线程的状态.png
创建线程
多线程的实现一般有以下3种方法
- 继承Thread类,重写run方法
public class MainThread extends Thread{
@Override
public void run() {
super.run();
}
}
- 实现Runnable接口,并实现该接口的run()方法
public class MainThread implements Runnable{
@Override
public void run() {
}
}
- 实现Callable接口,重写call()方法
public class MainThread implements Callable{
@Override
public Object call() throws Exception {
return null;
}
}
一般推荐用实现Runnable接口的方式,其原因是,一个类应该在其需要加强或者修改时才会被继承
线程的优先级
线程创建的时候,子线程继承父线程的优先级。
线程创建后,可通过setPriority方法改变优先级,默认线程优先级是5
线程的优先级是1-10的整数
常见的常量:Thread.MIN_PRIORITY(默认值1),Thread.MAX_PRIORITY(默认值10),Thread.NORM_PRIORITY(默认值5)
不能依靠线程的优先级来决定线程的执行顺序
线程的调度
线程调度器选择优先级最高的线程执行,以下情况会中断线程的执行
- 线程体中调用了yield方法,让出对CPU的占用权
- 线程体中调用了sleep方法,让线程进入睡眠状态
- 线程由于I/O操作而阻塞
- 另一个更高的优先级的线程出现
- 在支持时间片的系统中,该线程的时间片用完
多线程简介
多线程是指,一条以上的线程共同完成程序任务的方式。多线程可以提高CPU的利用率,进而提升整体的处理性能
多线程与单线程的对比:
利:程序整体性能提升,程序整体执行效率提高,程序整体体验改善
弊:引入数据同步的问题,过多的线程反而降低程序整体的性能
线程间数据共享与数据传递的方式
- 多条线程共用同一进程的内存资源,主要是通过共同的变量来进行数据共享的。
- 线程间可以通过启动线程时传入参数,使用管道流传输,通过Future和Callable获取线程执行的返回值来传输数据
多线程的数据同步问题
- 多条线程无共用数据时,相互独立运行,互不干扰,不会造成任何问题
- 当多条线程操作共用的数据时,非常容易出现因执行时间片用完而原子操作未执行完而导致的数据不一致
public class ThreadDemo {
static int count;
public static void main(String[] args) {
new Task().start();
new Task().start();
}
static class Task extends Thread{
@Override
public void run() {
int temp=count;//获取共享变量的值
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count=temp+1;//修改共享变量的值
System.out.println("count value is:"+count);
}
}
}
此时打印的结果是一样的,多线程同步与控制的方法,可以使用关键字synchronized关键字修饰代码,其中同步代码类型有:同步代码块,同步方法,同步静态方法,同步类
线程协同类有wait,notify,notifyall
死锁
原因:有时候两个或多个线程需要在共享对象上获取锁 这可能导致死锁
解决方法:使用一种名为资源排序的简单技术可以轻易的避免死锁。该技术给每一个需要锁的对象指定一个顺序,确保每个线程都按照这个顺序获取锁,就不会再发生死锁
线程池
ThreadPoolExecutor:线程池的核心实现类
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
- corePoolSize:核心线程数,默认情况下线程池是空的。如果当前运行的线程数少于corePoolSize,则会创建新线程来处理任务;如果多于或者等于corePoolSize,则不会创建
- maximumPoolSize:线程池允许创建的最大线程数。如果任务队列满了并且线程数小于maximumPoolSize,则线程池仍旧会创建新的线程来处理任务
- keepAliveTime:非核心线程闲置的超时时间,超过这个时间则回收。
- TimeUnit:keepAliveTime参数的时间单位。
- workQueue:任务队列。如果当前线程数大于corePoolSize,则将任务添加到此任务队列中。该任务
队列是BlockingQueue类型的,也就是阻塞队列 - workQueue:任务队列。如果当前线程数大于corePoolSize,则将任务添加到此任务队列中。该任务
队列是BlockingQueue类型的,也就是阻塞队列 - RejectedExecutionHandler:饱和策略。这是当任务队列和线程池都满了时所采取的应对策略,默认
是AbordPolicy,表示无法处理新任务,并抛出RejectedExecutionException异常。此外还有3种策略,它们分
别如下。
(1)CallerRunsPolicy:用调用者所在的线程来处理任务。此策略提供简单的反馈控制机制,能够减缓
新任务的提交速度。
(2)DiscardPolicy:不能执行的任务,并将该任务删除。
(3)DiscardOldestPolicy:丢弃队列最近的任务,并执行当前的任务。
线程处理流程
线程池的种类
- 1..FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
newFixedThreadPool的的corePoolSize和和maximumPoolSize的值都是nThreads,也就意味着FixedThreadPool只有核心线程,没有非核心线程池
- 2.CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
CachedThreadPool没有核心线程,非核心线程是无界的。keepAliveTime设置为60L,则空闲线程等待新任务的最长时间为 60s。在此用了阻塞队列 SynchronousQueue,它是一个不存储元素的阻塞队列
- 3.SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
SingleThreadExecutor是使用单个工作线程的线程池
- 4.ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
ScheduledThreadPool是一个能实现定时和周期性任务的线程池
AsyncTask源码分析
目的:AsyncTask异步刚开会加载方法-->然后执行什么方法-->最终会执行什么方法 异步加载只能调一次;
AsyncTask中有4个核心方法
- onPreExecute():在主线程中执行。一般在任务执行前做准备工作,比如对 UI 做一些标记。
- doInBackground(Params...params):在线程池中执行。在 onPreExecute方法执行后运行,用来执
行较为耗时的操作。在执行过程中可以调用publishProgress(Progress...values)来更新进度信息。 - onProgressUpdate(Progress...values):在主线程中执行。当调用
publishProgress(Progress...values)时,此方法会将进度更新到UI组件上。 - onPostExecute(Result result):在主线程中执行。当后台任务执行完成后,它会被执行。
AsyncTask的用法
AsyncTask asyncTask=new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
//请求网络,耗时操作,运行在Thread中
return null;
}
@Override
protected void onPreExecute() {
//一调用就会执行的方法,UI线程中
super.onPreExecute();
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
//执行完成后返回的方法,运行在UI线程
}
};
asyncTask.execute();
源码分析
首先看 asyncTask.execute();
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
//status默认的状态 private volatile Status mStatus = Status.PENDING;所以第一次不会走向这里
if (mStatus != Status.PENDING) {
switch (mStatus) {
//如果是RUNNING或者FINISHED状态则抛出异常
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
//设置当前状态为RUNNING,这也是为什么只能执行一次
mStatus = Status.RUNNING;
//首先会执行该方法
onPreExecute();
mWorker.mParams = params;
//execute是个接口,看mFuture是怎么被赋值的
exec.execute(mFuture);
return this;
}
AsyncTask<Params, Progress, Result>三个参数解释一下:Params为参数类型,Progress为后台任务执行进度的类型,Result为结果返回类型
//第一次new的时候就会走向这里
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//子线程中调用doInBackground
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
//最终会调用该方法
postResult(result);
}
return result;
}
};
//将mWorker设置为callable
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
我们可以知道FutureTask实际就是一个Runable,所以直接看run方法我们会发现实际最终调用的
result = c.call();//c实际就是callable而callable实际就是调用WorkerRunnable中的call方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
//Handler发送消息,切换到主线程
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
最终会走到这里
private void finish(Result result) {
if (isCancelled()) {
//如果已经取消,调用oncancelled方法
onCancelled(result);
} else {
//主线程调用该方法
onPostExecute(result);
}
//设置状态为FINISHED
mStatus = Status.FINISHED;
}
总结:.execute一调用就会判断当前状态如果状态不对就会抛出异常,然后设置状态为RUNNING,然后执行onPreExecute(), 开一个线程执行 doInBackground(), doInBackground()执行完毕之后会利用Handler发送消息切换主线程中,然后执行onPostExecute()方法,最后把状态置为FINISHED
参考书籍:Android进阶之光》
网友评论