线程基础、同步、阻塞队列、线程池、AsynTask异步任务
一:线程基础
1.进程与线程
进程:是操作系统结构的基础,是程序在一个数据集合上的运行过程,是系统进行资源分配和调度的基本单位。
线程:是操作系统调度的最小单位。
2.线程的状态
New:新创建状态。
Runnable:可运行状态。(注意:此状态不一定在运行,主要取决于cpu是否给线程运行起来)
Blocked:阻塞状态。表示被锁阻塞,他暂时不活动。
Waiting:等待状态。线程暂时不活动。
Timed waiting:超时等待状态。和等待状态不同,在规定时间内会被自动唤醒。
Terminated:终止状态。表示线程已经执行完毕。
具体关于状态详细内容可以参考:
3.创建线程
①.继承Thread类,实现run()方法
②.实现Runnable方法,并实现接口的run()方法
③.实现Callable接口,重写call()方法。是线程池中的方法,Callable属于Executor框架功能类
一般推荐使用第二种,实现Runnable方法。
4.理解中断
①.Thread.currentThread.isInterrupted()方法使其线程中断
②.在线程处于阻塞情况下中断标识符为true会抛出异常InterruptedException,应该对异常进行处理
③.调用中断方法并不一定会中断,如果线程比较重要,则不会被中断。
二:同步
同步:解决线程高并发问题。
1.重入锁与条件对象
重入锁ReentrantLock(相当于synchronized)是Java 5.0引入的,重入锁结构确保任何时刻只有一个线程进入临界区。如下所示结构
临界区就是同一时刻只能有一个任务访问的代码区。
注意在finally释放锁。如下所示。
Lock mLock = new ReentrantLock();
mLock.lock();
try{
...
}
finally{
mLock.unlock();
}
一个锁对象可以获得多个条件对象,可以用newCondition方法获得一个条件对象。
得到条件对象后调用await方法(相当于wait方法),当前线程就放弃锁,进入等待队列。
同一条件对象调用signalAll(相当于notifyAll)方法会重新激活这一条件等待的所有线程。
同一条件对象调用signal(相当于notify)方法会随机激活一个同一对象的线程。
同步代码块一般比较脆弱,尽量使用同步方法。
2.volatile
volatile关键字为实例域的同步访问提供了免锁机制。
①.Java内存模型抽象概念:线程的共享变量存在主存中,每个线程有一个私有的本地内存,本地内存存储着共享变量的副本。如下图:
②.并发编程的三个特性:原子性、可见性和有序性
原子性:操作不可中断
可见性:一个线程修改状态对另一个线程可见
有序性:Java内存模型中允许编译器和处理器对指令进行重排序,禁止重排序就是具有有序性
volatile保证可见性、有序性;不保证原子性
正确使用关键字volatile:
①.对变量的写操作不能依赖当前值。
②.该变量没有包含在其他变量的不变式中。
本质:volatile变量操作中不能有volatile变量,因为保证不了原子性。
volatile使用场景:
①.状态标志
②.双重检查模式
三:阻塞队列
1.阻塞队列:常用于生产者和消费者的场景,生产者往队列里面放元素,消费者从队列里面拿元素。
2.Java中提供七种阻塞队列:
ArrayBlockingQueue:由数组结构组成的有界阻塞队列。(重点)
LinkedBlockingQueue:有链表结构组成的有界阻塞队列。(重点)
PriorityBlockingQueue:支持优先级的无解阻塞队列。
DelayQueue:支持延时获取元素的无界阻塞队列。(重点)
SynchronousQueue:不存储元素的阻塞队列。(重点)
LinkedTransferQueue:由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:由链表结构组成的双向阻塞队列。
3.阻塞队列的实现:内存通过重入锁与条件对象的等待和解除等待机制。
4.使用场景:生产者消费者场景。
四.线程池
1.ThreadPoolExecutor线程池拥有最多参数的构造方法如下代码:
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //闲置时间
TimeUnit unit, //时间单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory, //线程工厂:主要给线程起名
RejectedExecutionHandler handler) {...} //饱和策略
2.线程池的处理流程及原理:
如上图,执行ThreadPoolExecutor的executor方法,会遇到各种情况:
①.如果线程池未达到核心线程数,创建核心线程处理任务。
②.线程池超过核心线程数,将任务放入任务队列中,线程池中有空闲线程会不断地从任务队列中取出任务进行处理。
③.如果任务队列满了,并且线程数没有达到最大线程数,则创建非核心线程去处理任务。
④.如果线程数超过了最大线程数,执行饱和策略。
2.线程的种类:
①.FixedThreadPool:固定可重用线程数的线程池。构造方法如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, //核心线程数nThreads个
nThreads, //最大线程数nThreads个,非核心线程0个
0L, TimeUnit.MILLISECONDS, //闲置时间0
new LinkedBlockingQueue<Runnable>()); //链表阻塞队列
}
②.CacheThreadPool:根据需要创建的线程池,适合于大量且耗时短的任务,构造方法如下:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, //核心线程0个,非核心线程无界
60L, TimeUnit.SECONDS, //空线程等待任务时间60s(最多闲置60s)被销毁
new SynchronousQueue<Runnable>()); //不存储元素的阻塞队列
}
③.SingleThreadExecutor:单个工作线程的线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
④.ScheduledThreadPool:实现定时和周期任务的线程池,最终调用构造方法如下
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue()); //延时队列
}
五.AsyncTask原理:
1.AsyncTask五个核心方法:
onPreExecute():在主线程执行,一般是在任务执行之前做准备工作。
doInBackground(Params...params):在线程池中执行,用来执行耗时操作。调用publishProgress(Progress...values)来更新信息。
onProgressUpdate(Progress... values):在主线程执行,当调用publishProgress(Progress...values)时此方法用来更新ui组件。
onPostExecute(Result result):在主线程中执行。当后台任务执行完成就调用此方法,doInBackground的返回值就是参数result。
onCancelled(Result result):主线程中执行。如果任务被取消此方法就会被调用,doInBackground的返回值就是参数result。
2.android 3.0之前是并行执行任务,当提交139个任务会执行饱和策略。android 3.0之后是串行执行任务,不会发生饱和。
3.异步任务原理是:线程池+Handler机制
4.也可以调用asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"")执行并行策略。
5.可以改变线程池还可以传入自定义线程池:本质都是调用executeOnExecutor()方法。