多线程

1.为什么要使用多线程?多线程的优点和缺点是什么?

首先说下多线程出现的原因:

为了解决负载均衡问题,充分利用CPU资源.为了提高CPU的使用率,采用多线程的方式去同时完成几件事情而不互相干扰.为了处理大量的IO操作时或处理的情况需要花费大量的时间等等,比如:读写文件,视频图像的采集,处理,显示,保存等

多线程的好处:

1.使用线程可以把占据时间长的程序中的任务放到后台去处理

2.用户界面更加吸引人,这样比如用户点击了一个按钮去触发某件事件的处理,可以弹出一个进度条来显示处理的进度

3.程序的运行效率可能会提高

4.在一些等待的任务实现上如用户输入,文件读取和网络收发数据等,线程就比较有用了.

多线程的缺点:

1.如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换.

2.更多的线程需要更多的内存空间

3.线程中止需要考虑对程序运行的影响.

4.通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生;

2.Java中多线程安全问题

同时满足以下两个条件时:

1,多个线程在操作共享的数据。2,操作共享数据的线程代码有多条。

当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。

解决:

将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。

当前线程把这些代码都执行完毕后,其他线程才可以参与运算。

在java中,用同步代码块就可以解决这个问题。

同步代码块的格式:synchronized(对象){需要被同步的代码;}

这个对象一般称为同步锁。

同步的前提:同步中必须有多个线程并使用同一个锁。

同步的好处:解决了线程的安全问题。

同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。

AsyncTask的原理

AsyncTask的实现原理 = 线程池 + Handler

AsyncTask类属于抽象类,即使用时需 实现子类

publicabstractclassAsyncTask{...}// 类中参数为3种泛型类型// 整体作用:控制AsyncTask子类执行线程任务时各个阶段的返回类型// 具体说明:// a. Params:开始异步任务执行时传入的参数类型,对应excute()中传递的参数// b. Progress:异步任务执行过程中,返回下载进度值的类型// c. Result:异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致// 注:// a. 使用时并不是所有类型都被使用// b. 若无被使用,可用java.lang.Void类型代替// c. 若有不同业务,需额外再写1个AsyncTask的子类

线程的原理

corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即便其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程

ThreadPoolExecutor创建基本线程池

答:创建线程池,主要是利用ThreadPoolExecutor这个类,而这个类有几种构造方法,其中参数最多的构造方法如下:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue workQueue, ThreadFactory threadFactory) {

... }

corePoolSize:该线程池中核心线程的数量.

maximumPoolSize:该线程池中最大线程数量(区别与corePoolSize).

keepAliveTime:从字面上就可以理解,是非核心线程空闲时要等待下一个任务到来的时间,当任务很多,每个任务执行时间很短的情况下调大该值有助于提高线程利用率,注意:当allowCoreThreadTimeOut属性设为true时,该属性也可用于核心线程.

unit:上面时间属性的单位

workQueue:任务队列

threadFactory:线程工厂,克永于设置线程名字等等,一般无须设置该参数

设置好几个参数就可以创建一个基本的线程池,而之后的各种线程池都是在这种基本线程池的基础上延伸的!

execute一个线程之后,如果线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行

2.execute一个线程之后,如果线程池中的线程数已经达到核心线程数,且workQueue已满,则将新线程放入workQueue中等待执行

3.execute一个线程之后,如果线程池中的线程数已经达到核心线程数但未超过非核心线程数,且workQueue已满,则启用一个非核心线程来执行任务

4.execute一个线程之后,如果线程池中的线程数已经超过非核心线程数,则拒绝执行该任务,采取饱和策略,并抛出RejectedExrcutionException异常

1.newCachedThreadPool:

底层:返回ThreadPoolExecutor实例,corePlloSize为0;

maximumPoolSize为Integer.MAX_VALUE;

keepAliveTime为60L;

unit为TimeUnit.SECONDS;

workQueue为SynchronousQueue(同步队列)

通俗来讲:当有新任务到来,则插入SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务,若池中线程空闲时间超过指定大小,则该线程会被销毁!

适用:执行很多短期异步的小程序或者负载较轻的服务器

2.newFixedThreadPool:

底层:返回ThreadPoolExecutor实例,接收参数为所设定线程数量nThread,corePoolSize为nThread,

maximumPoolSize为nThread;keepAliveTime为OL(不限时);

unit为:TimeUnit.MILLISECONDS;

WorkQueue为:new LinkedBlockingQueue()无解阻塞队列

通俗来讲:创建可容纳固定数量线程的池子,每隔线程的存活时间是无限的,当池子满了就不在添加线程了,如果池中的所有线程均载繁忙状态,对于新任务会进入阻塞队列中(无界的阻塞队列)

适用:执行长期的任务,性能好很多

3.newSingleThreadExecutor:

底层:FinalizableDelegatedExecutorService包装的ThreadPoolExecutor实例,corePoolSize为1,

maximumPoolSize为1;keepAliveTime为0L;

unit为:TimeUnit.MILLISECONDS;

workQueue为:new LinkedBlockingQueue() 无解阻塞队列

通俗来讲:闯将只用一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)

适用:一个任务一个任务执行的场景

4.NewScheduledThreadPool

底层:创建ScheduledThreadPoolExecutor实例,corePoolSize为传递来的参数,

maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为0;

unit为:TimeUnit.NANOSECONDS;

workQueue为:new DelayedWorkQueue() 一个按超时时间升序排序的队列

通俗来讲:创建一个固定大小的线程池,线程池内线存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程骏处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时间排序的队列结构

使用:周期性执行任务的场景

多线程 和异步任务

多线程的意义

多线程的创建

多线程的应用

异步任务处理机制

异步任务

为什么要使用多线程

1.提高用户的体验或者说避免我们的ANR

在事件处理代码中需要使用多线程,否则会出现ANR(Application is not responding)

应用程序无响应或者 因为响应时间较长导致用户的体验很差。‘

2,异步

应用中有些情况下并不一定需要同步阻塞去等待返回结果,可以通过多线程来实现异步。例如

你的应用中的某个Activity 需要从云端获取一些图片,加载图片比较耗时,这时候需要使用

异步加载,加载完成一个图片刷新一个。

3.多任务

例如多线程的下载。

ANR

意思就是应用程序无响应,如果一个 应用无法响应用户的输入,系统就会弹出ANR对话框

如下图标所示用户可以自行选择等待,抑或是停止当前程序

AsyncTask 是Android框架提供的异步处理的辅助类,它可以实现耗时操作在其它线程执行

而处理结果在主线程处理,。

线程池概念

在JAVA中创建和销毁对象都是比较消耗资源的,我们如果在应用中频繁的创建和销毁我们的thread对象

就会产生很多临时对象 当失去引用的临时对象较多,虚拟机会进行GC(垃圾回收)CPU在进行GC时会

导致应用程序的运行得不到响应,从而导致应用的响应性降低。

线程池就是解决这一个问题,当你需要使用对象时候就从线程池中取出来,线程池负责维护对象的生命周期

new Thread(){

run();

}.start()的弊端

1 每次new thread 性能差

2 线程缺乏统一的管理可能无限制新建新线程,相互之间竞争占用过多的系统资源导致死机或者oom内存溢出

3 缺乏过多功能 如定时执行 定期执行 线程中断

线程池的好处

1 重用存在的线程,减少对象创建,销毁的开销性能较好

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

  避免阻塞

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

线程池构造方法核心参数

corePoolSize:线程池的核心线程数目,当一个请求进来时如果当前线程池中线程数量小于这个值,则直接通过ThreadFactory新建一个线程来处理这个请求,如果已有线程数量大于等于这个值则将请求放入阻塞队列中。

maximumPoolSize:线程池的最大线程数目,当线程池数量已经等于corePoolSize并且阻塞队列也已经满了,则看线程数量是否小于maximumPoolSize:如果小于则创建一个线程来处理请求,否则使用“饱和策略”来拒绝这个请求。对于大于corePoolSize部分的线程,称作这部分线程为“idle threads”,这部分线程会有一个最大空闲时间,如果超过这个空闲时间还没有任务进来则将这些空闲线程回收。

keepAliveTime和unit:这两个参数主要用来控制idle threads的最大空闲时间,超过这个空闲时间空闲线程将被回收。这里有一点需要注意,ThreadPoolExecutor中有一个属性:private volatile boolean allowCoreThreadTimeOut;,这个用来指定是否允许核心线程空闲超时回收,默认为false,即不允许核心线程超时回收,核心线程将一直等待新任务。如果设置这个参数为true,核心线程空闲超时后也可以被回收。

workQueue:阻塞队列,超过corePoolSize部分的请求放入这个阻塞队列中等待执行。阻塞队列分为有界阻塞队列和无界阻塞队列。在创建阻塞队列时如果我们指定了这个队列的“capacity”则这个队列就是有界的,否则是无界的。这里有一点需要注意:使用线程池之前请明确是否真的需要无界阻塞队列,如果阻塞队列是无界的,会导致大量的请求堆积,进而造成内存溢出系统崩溃。

threadFactory:是一个线程池工厂,主要用来为线程池创建线程,我们可以定制一个ThreadFactory来达到统一命名我们线程池中的线程的目的。

handler:饱和策略,用来拒绝多余的请求。饱和策略有:CallerRunsPolicy:请求脱离线程池运行(调用者caller线程来运行这个任务);AbortPolicy:抛出RejectedExecutionException异常;DiscardPolicy:丢弃这个任务,即什么也不做;DiscardOldestPolicy:将阻塞队列中等待时间最久的任务删除(即队列头部的任务),将新的任务加入队尾。

线程池提供了4种类型的线程池

newCachedThreadPool

newFixedThreadPool

newSingleThreadPool

newScheduledThreadPool

所有的线程池都是我们的一个ExecutorService的子类 通过

举例:

ExecutorService threadPool=Executors.newCachedThreadPool();

来创建我们的一个线程池实例

newCachedThreadPool创建一个可缓存的线程池如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收

则新建线程

newFixedThreadPool 创建一个定长的线程池,可控制最大的并发数,超出的线程会在等待队列中等待

newSingleThreadPool单线程化线程池 只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO LIFO 优先级)

执行

newScheduledThreadPool 创建一个定长的线程池 支持定时及周期性任务执行

切换线程到主线程的方法

handler.sendMessage()

handler.post(new Runnable())

view.post(new Runnable) 控件的post方法也可以切换到主线程

其实内部实现原理也是通过handler.post()

activity.runOnUiThread();

异步任务

为了更加方便我们在子线程中跟新UI元素,Android从1.5版本就引入了一个AsyncTask类,

使用它就可以非常灵活的从子线程切换到我们的主线程

AsyncTask允许我们的执行一个异步任务在后台,我们可以将耗时操作放在异步任务当中

并随时将任务执行的结果返回给我们的UI线程来更新我们的UI控件。

通过AsyncTask我们可以轻松解决多线程之间的通讯问题。

简单来说AsynTask就是Android 给我们开发者提供了一个多线程编程的框架,其介于

Thread 和Handler之间 我们如果要定义一个AsyncTask 就需要继承AsyncTask抽象类

并实现唯一的一个 doInBackgroud()抽象方法;

AsyncTask 三个泛型参数

1 params 执行异步任务所需要传入的事件参数 用于后台任务中使用

2 progress 后台任务执行时,如果需要在界面上显示当前进度,则使用这里指定的泛型作为进度单位

3Result 结果 当任务执行完毕之后 如果需要对结果进行返回, 则使用这里指定的泛型作为返回值类型

protect  Result(类型)  doInBackground(params(类型)){

//这里是我们的异步线程处理异步任务

return result(类型);

}

protected void onPostExecute(Result(类型)){

//这里的线程是我们的主线程 执行我们的修改ui的操作

}

我们通过 AsyncTask对象.execute()开启执行我们的异步任务

其它的重写方法

onPreExecute()

这个方法会在后台任务开始执行之前调用,用于进行初始化操作。比如在界面上显示

一个进度条对话框

onProgressUpdate(Progress)

当在异步任务中调用了publishProgress(Progress)通知主线程我们当前的进度是多少参数传递是我们的实时进度类型

后这个方法就很快被调用方法中携带的参数就是在后台任务中携带过来的在这个方法中可以对Ui进行操作

利用Progress 对Ui进行修改跟新进度

onPostExecute(Result)

当后台执行完任务完毕后调用

你可能感兴趣的:(多线程)