Android进程、线程与线程池浅析

1、线程

1.1、概念

线程是系统分配处理器时间资源的基本单元也是系统调用的基本单位,简单理解就是一个或多个线程组成了一个进程。

1.2、实现方式(三种)

1.2.1、继承Thread线程类

a、定义一个类MyThread继承Thread,并重写run方法。

b、将要执行的代码写在run方法中。

c、创建该类的实例,并调用start()方法开启线程。

1.2.2、实现Runnable接口

a、定义一个类MyRunnable实现Runnable接口,并重写run方法。

b、将要执行的代码写在run方法中。

c、创建Thread对象, 传入MyRunnable的实例,并调用start()方法开启线程。

1.2.3、实现 Callable 接口

Callable 是类似于 Runnable 的接口,实现 Callable 接口的类和实现 Runnable 的类都是可被其它线程执行的任务。

a、自定义一个类 MyCallable 实现 Callable 接口,并重写call()方法

b、将要执行的代码写在call()方法中

c、创建线程池对象,调用submit()方法执行MyCallable任务,并返回Future对象

d、调用Future对象的get()方法获取call()方法执行完后的值

1.3、生命周期

用new Thread()的方法新建一个线程,在线程创建完成之后,线程就进入了就绪(Runnable)状态(start()),此时创建出来的线程进入抢占CPU资源的状态,当线程抢到了CPU的执行权之后,线程就进入了运行状态(Running)(run()),当该线程的任务执行完成之后或者是非常态的调用的stop()方法之后,线程就进入了死亡状态。此外还有一种线程阻塞的状态,当线程主动调用了sleep()方法时,线程会进入阻塞状态;当线程中主动调用了阻塞时的IO方法时,这个方法有一个返回参数,当参数返回之前,线程也会进入阻塞状态;当线程进入正在等待某个通知时,会进入阻塞状态。线程在阻塞过程结束之后,会重新进入就绪状态,重新抢夺CPU资源。

出现阻塞状态的原因,CPU的资源是十分宝贵的,所以当线程正在进行某种不确定时长的任务时,Java就会收回CPU的执行权,从而合理应用CPU的资源。

1.4、拓展多线程

1.4.1、概念

多线程,创建多条线程同时执行任务。

1.4.2、并行与并发的区别

并行,则是真正意义上的同时进行多种事情,这种只可以在多核CPU的基础下完成。即多个处理器或者多核处理器同时执行多个不同的任务。

并发,从宏观方面来说,并发就是同时进行多种任务,实际上,这几种任务,并不是同时进行的,而是交替进行的,而由于CPU的运算速度非常的快,会造成我们的一种错觉,就是在同一时间内进行了多种事情。即一个处理器处理多个任务。

1.4.3、多线程的安全

问题:CPU的使用权抢占和资源的共享发生了冲突,即一个线程在操作共享数据的过程中CPU切换到其他线程又对该数据进行操作,这就是所谓的多线程并发。

解决方法:我们只需要让一条线程占据了CPU的资源时,阻止第二条线程同时抢占CPU的执行权,在代码中,我们只需要在方法中使用同步代码块即可。即把操作数据的那段代码用 synchronized 进行同步, 这样就能保证在同一时刻只能有一个线程能够访问。

2、线程池

2.1、概念

线程池就是用来管理线程的,它的好处,就是可以方便的管理线程,也可以减少内存的消耗(核心回收循环利用)。线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。一个比较简单的线程池至少应包含线程池管理器、工作线程、任务列队、任务接口等部分。其中线程池管理器的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务是进行等待;任务列队的作用是提供一种缓冲机制,将没有处理的任务放在任务列队中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。

2.2、线程池中的类

a、Executor

java中线程池的顶级接口,可以称它为一个执行器。他只有一个简单的方法execute(Runnable command),就是用来执行提交的任务。

b、ExecutorService

Executor的子类,也是真正的线程池接口。它提供了提交任务和关闭线程池等方法。调用submit方法提交任务还可以返回一个Future对象,利用该对象可以了解任务执行情况,获得任务的执行结果或取消任务。

c、Executors

由于线程池配置比较复杂,自己配置的线程池可能性能不是最好的。Executors就是用来方便创建各种常用线程池的工具类。

d、ThreadPoolExecutor

ExecutorService的默认实现,Executors创建各种线程池的时候内部其实就是调用了ThreadPoolExecutor的构造方法。

构造函数说明:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,RejectedExecutionHandler handler){};

corePoolSize就是线程池中的核心线程数量,这几个核心线程,就算在没有用的时候,也不会被回收;maximumPoolSize就是线程池中可以容纳的最大线程的数量;keepAliveTime就是线程池中除了核心线程之外的其他的最长可以保留的时间,因为在线程池中,除了核心线程即使在无任务的情况下也不能被清除,其余的都是有存活时间的,意思就是非核心线程可以保留的最长的空闲时间;util就是计算这个时间的一个单位;workQueue就是等待队列,任务可以储存在任务队列中等待被执行,执行的是FIFIO原则(先进先出);threadFactory就是创建线程的线程工厂;handler是一种拒绝策略,我们可以在任务满了拒绝执行某些任务。

2.3、执行步骤

任务进来时,首先执行判断,判断核心线程是否处于空闲状态,如果不是,核心线程就先就执行任务,如果核心线程已满,则判断任务队列是否有地方存放该任务,若果有,就将任务保存在任务队列中,等待执行,如果满了,在判断最大可容纳的线程数,如果没有超出这个数量,就开创非核心线程执行任务,如果超出了,就调用handler实现拒绝策略。

注:handler的拒绝策略(四种)

第一种AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满

第二种DisCardPolicy:不执行新任务,也不抛出异常

第三种DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行

第四种CallerRunsPolicy:直接调用execute来执行当前任务

2.4、常见线程池(四种)

CachedThreadPool:可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况。(创建带有缓存的线程池,在执行新的任务时,当线程池中有之前创建的可用线程就重用之前的线程,否则就新建一条线程。如果线程池中的线程在60秒未被使用,就会将它从线程池中移除。)

SecudleThreadPool:周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。(创建定时和周期性执行的线程池。)

SingleThreadPool:只有一条线程来执行任务,适用于有顺序的任务的应用场景。(创建一个单线程的线程池,即这个线程池永远只有一个线程在运行,这样能保证所有任务按指定顺序来执行。如果这个线程异常结束,那么会有一个新的线程来替代它。)

FixedThreadPool:定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程。(创建固定大小的线程池,这样可以控制线程最大并发数,超出的线程会在队列中等待。如果线程池中的某个线程由于异常而结束,线程池则会再补充一条新线程。)

3、进程与线程的比较

进程是一个动态的过程,是一个活动的实体,是正在执行的程序。一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执行者,可以说,进程中包含了多个可以同时运行的线程,线程是进程的一部分;进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值