一.引子:
在Android默认主线程(UI线程)不能进行耗时操作的,如果在主线程中进行了耗时的操作会导致ANR,所以在很多的操作必然会开启新的线程去做耗时的操作。
二.线程的开启有两种方式及线程相关知识:
1.继承Thread类创建线程类:
首先,定义Thread类的子类,并重写该类的run()方法,这个方法就代表了线程要完成的任务,因此把run()方法称为执行体;再者,创建Thread子类的实例,即创建线程对象;最后,调用线程对象start()方法启动该线程。
2.实现Runnable接口创建线程类:
首先定义Runnable接口的实现类,并重写该接口run()方法的方法体;再者,创建Runnable实现类的实例,并以此作为Thread来创建对象,这个就是真正的线程对象;最后调用线程对象的start()方法启动该线程。
3.线程的共享资源:
在Thinking in java中有这样一段文字“对一种特殊的资源—— 对象中的内存—— Java 提供了内建的机制来防止它们的冲突。由于我们通常将数据元素设为从属于 private(私有)类,然后只通过方法访问那些内存,所以只需将一个特定的方法设为synchronized(同步的),便可有效地防止冲突。在任何时刻,只可有一个线程调用特定对象的一个synchronized 方法(尽管那个线程可以调用多个对象的同步方法)。每个对象都包含了一把锁(也叫作“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代码)。调用任何 synchronized 方法时,对象就会被锁定,不可再调用那个对象的其他任何synchronized 方法,除非第一个方法完成了自己的工作,并解除锁定。”这句话大有裨益啊!想保护其他某些资源不被多个线程同时访问时,可以强制通过synchronized防止访问那些资源。
4.阻塞及死锁:
Thinking in java描述了五种可能造成阻塞的原因:
- 调用sleep()使线程进入睡眠状态;
- supend()暂停线程执行;
- wait()暂停线程执行;
- 线程正在等候IO操作;
- 线程试图调用另一个对象的同步方法,但那个对象处于锁定状态,暂时无法使用。
死锁问题:如果A线程执行同步代码块,在同步代码块中又有同步。所以A线程拥有object锁的同时又获得了this锁,此时B线程进行同步函数,由于A已经获取了this的锁,两个都在等待对象释放this锁。这样就产生了死锁。
三.子线程回调主线程的方式:
- view.post(Runnable action);
- ativity.runOnUiThread(Runnable action);
- Handler的消息机制:
作用:
是由于主线程中操作会导致ANR异常,所以需要将网络请求耗时操作放在子线程中进行,但是子线程中不能操作UI,所以需要将子线程中获取到的数据传递到UI线程中进行UI更新。这样Handler机制及应运而生。
使用:
首先在主线程中创建Handler对象,重写handlerMessage方法。用该handler对象在子线发送消息,然后在主线程的handlerMessage方法中处理消息。接下来handler.post(Runnable runnable)。
组成:
handler消息机制有四大组成部分,分别是:
- Handler:用于发送消息和处理消息。
- Message:用于携带数据和通知。
- MessageQueue:用于储存消息,它内部是单链表实现的
- Looper:无限循环的轮训器,用于从MessageQueue中取出消息,并交给Handler处理。
在主线程中默认是初始化了Looper,在子线程中并没有这样做。如果要实现主线程向子线程中传递数据,那么必须在子线程中new一个Handler对象,然后用该handler对象然后用该handler对象在主线程发送消息到子线程。注意的是必须也要在子线程中进行Looper的初始化Looper.prepare和Looper.loop,这样才能实现主线程向子线程发送消息。
四.线程优化:
1.优化的起因:
在程序中大量的创建线程势必会产生大量线程开启和关闭的操作,这样会带来性能开销。所以才有了线程这种方法对此进行优化。线程池内部重用线程,可避免创建和销毁带来的性能开销,同时还能控制线程池最大并发数,避免大量线程因互相抢占系统资源导致阻塞现象。线程池的就是事先将多个线程对象放在一个容器中,当使用时不用new线程而是直接去线程池中拿即可,省了开辟子线程的时间。
2.好处:
降低资源消耗,通过重复利用,提高响应速度,提高线程的可管理性。
3.启动策略:
- 线程池创建时,里面是没有一个线程。任务队列是作为参数传进来的,不过,就算队列里有任务,线程池也不会马上执行它们。
- 当调用excute()方法添加一个任务是线程池会做出判断:
(1).如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行任务。
(2).如果这时候的线程数量大于或等于corePoolSize,那么放入队列队列中。
(3).队列满时,而且正在运行线程数小于maximumPoolSize,那么还是创建运行。
(4).队列满时,而且正在运行线程大于或等于maximumPoolSize,那么会抛出异常告诉调用者,不在接受任务。
- 当一个线程完成任务时,它会从队列取下一个任务来执行。
- 当线程池无事可做时,超过一段时间时线程池会做出判断,如果当前运行的线程数大于corePoolSize,那么这些线程会别停掉,所以线程池的所有任务完成后,它最终收缩到corePoolSize的大小。
4.在Android中有这么一个ThreadPoolExecutor类,其大概由四个基本组成部分:
1).线程管理器ThreadPool:主要功能是创建、管理、添加线程;
2).工作线程PoolWorker:在没有线程池处于等待循环执行;
3).任务接口Task:主要规定任务入口、执行状态;
4).任务队列TaskQueue:用于存放没有处理的任务,提供缓存。
5.ThreadPoolExecutor类构造方法中有五个重要的参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
1).corePoolSize:核心线程数,一直存活并处于闲置;
2).maximumPoolSize:最大线程数,达到最大任务数新任务将会被阻塞;
3).keepAliveTime:非核心线程闲置时超时时长;
4).workQueue:任务通过线程池的excute方法提交的Runnable对象会存储在这个参数中;
5).threadFactory:线程工厂
6.java中Excutor类中默认创建四种类型的线程池:
1).newFixThreadPool:它是一种线程固定的线程池,当线程处于空闲状态时,它不用被回收除非线程池被关闭。
2).newCachedThreadPool:线程数量不定的线程池,它只有非核心线程。
3).newScheduledThreadPool:核心线程数量固定,非核心线程没有限制,非核心线程闲置时会被回收。
4).newSingleThreadExecutor:只有核心线程,它确保所有任务都在同一个线程中按顺序执行。
7.自己实现了一个ThreadManager的线程管理类:
package com.example.foreveross.clickdemo;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Created by wangyongyao on 2017/4/15.
*/
public class ThreadManager {
// 定义两个池子,mNormalPool 访问网络用的,mDownloadPool 是下载用的
private static ThreadPoolProxy mNormalPool = new ThreadPoolProxy(1, 3, 5 * 1000);//param 0 最大线程数,param 1 核心线程数
private static ThreadPoolProxy mDownloadPool = new ThreadPoolProxy(3, 3, 5 * 1000);
// proxy 是代理的意思
// 定义两个get方法,获得两个池子的对象 ,直接get 获得到的是代理对象
public static ThreadPoolProxy getNormalPool() {
return mNormalPool;
}
public static ThreadPoolProxy getDownloadPool() {
return mDownloadPool;
}
// 代理设计模式类似一个中介,所以在中介这里有我们真正想获取的对象
// 所以要先获取代理,再获取这个线程池
public static class ThreadPoolProxy {
private final int mCorePoolSize; // 核心线程数
private final int mMaximumPoolSize; // 最大线程数
private final long mKeepAliveTime; // 所有任务执行完毕后普通线程回收的时间间隔
private ThreadPoolExecutor mPool; // 代理对象内部保存的是原来类的对象
// 赋值
public ThreadPoolProxy(int corePoolSize, int maximumPoolSize, long keepAliveTime) {
this.mCorePoolSize = corePoolSize;
this.mMaximumPoolSize = maximumPoolSize;
this.mKeepAliveTime = keepAliveTime;
}
private void initPool() {
if (mPool == null || mPool.isShutdown()) {
// int corePoolSize = 1;//核心线程池大小
// int maximumPoolSize = 3;//最大线程池大小
// long keepAliveTime = 5 * 1000;//保持存活的时间
TimeUnit unit = TimeUnit.MILLISECONDS;//单位
BlockingQueue<Runnable> workQueue = null;//阻塞队列
workQueue = new ArrayBlockingQueue<Runnable>(3);//FIFO,大小有限制,为3个
//workQueue = new LinkedBlockingQueue(); //队列类型为linked,其大小不定,无限大小
// workQueue = new PriorityBlockingQueue();
ThreadFactory threadFactory = Executors.defaultThreadFactory();//线程工厂
RejectedExecutionHandler handler = null;//异常捕获器
// handler = new ThreadPoolExecutor.DiscardOldestPolicy();//去掉队列中首个任务,将新加入的放到队列中去
// handler = new ThreadPoolExecutor.AbortPolicy();//触发异常
handler = new ThreadPoolExecutor.DiscardPolicy();//不做任何处理
// handler = new ThreadPoolExecutor.CallerRunsPolicy();//直接执行,不归线程池控制,在调用线程中执行
// new Thread(task).start();
// 创建线程池
mPool = new ThreadPoolExecutor(mCorePoolSize,
mMaximumPoolSize,
mKeepAliveTime,
unit,
workQueue,
threadFactory,
handler);
}
}
/**
* 执行任务
* @param task
*/
public void execute(Runnable task) {
initPool();
//执行任务
mPool.execute(task);
}
// 提交任务
public Future<?> submit(Runnable task) {
initPool();
return mPool.submit(task);
}
// 取消任务
public void remove(Runnable task) {
if (mPool != null && !mPool.isShutdown()) {
mPool.getQueue()
.remove(task);
}
}
}
}