Android 总结2-线程

Posted by Jfson on 2019-01-05

1.Android 中的多线程

  • 1.继承Thread
  • 2.实现Runnable
  • 3.Handler
  • 4.AnsyTask
  • 5.HandlerThread

ActivityThread 为 Android 主线程

2.AnsyTask

  • AsyncTask 最好在主线程中初始化。因为AsyncTask需要Handler来将执行结果回调切回主线程,Handler 中Looper.getMainLooper()。所以异步线程初始化也没关系

  • excute()最好在主线程中初始化。并且一个AsyncTask只能执行一次excute(),否则会报错。其内部有一个枚举类型的Status用于维护执行状态:PENDING、RUNNING、FINISHED。默认情况下是PENDING,表示可以执行,当调用execute方法之后,会检查其状态是否是PENDING,如果不是的话就会抛出异常。

  • 经过测试。AsyncTask 可以在子线程初始化 和 excute() 可以在子线程中进执行

  • Android 1.6之前为串行,之后改为并行,3.0由改为串行。

    • execute()串行执行 —>调用executeOnExecutor(),并传入一个sDefaultExecutor(串行线程池)

    • sDefaultExecutor是SerialExecutor的一个实例,而且它是个静态变量。也就是说,一个进程里面所有AsyncTask对象都共享同一个SerialExecutor对象。

    • asyncTestThread.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,””); 并行执行最大5个线程。(固定线程数量的线程池)

      AsyncTask.THREAD_POOL_EXECUTOR。也可以使用自己的线程池。

  • AsyncTask 中有两个线程池,SerialExecutor 和用于任务排队的线程池。

3.HandlerThread

  • 继承自Thread,是一个子线程中创建了Handler
  • HandlerThread 中run是无限循环,不需要时可以挂起,
  • 在getLooper 中会try catch,避免looper为空
  • 获取子线程创建Handler
1
2
3
4
5
6
7
8
9
10
//创建HandlerThread实例
HandlerThread mHandlerThread = new HandlerThread("handler_thread");
//开始运行线程
mHandlerThread.start();
//获取HandlerThread线程中的Looper实例
Looper loop = mHandlerThread.getLooper();
//创建Handler与该线程绑定。
mSubThreadHandler = new Handler(loop){
public void handleMessage(Message msg) {}
}

4.ThreadLocal

ThreadLocal 存储器,做到只有该线程自己能够访问。就是多个线程访问同一份数据,每个线程得到数据不一致。理解为一个 Map,它的 set 和 get 方法都有一个“隐形的” key,那就是当前的线程对象,所以它才可以为每个线程保存一个数据副本。

5.IntentService

IntentService 继承自Service,并且是一个抽象类。IntentService封装了Handler和HandlerThread,所以这个Handler是子线程中的,适合执行耗时后台任务。

stopSelf(int startId) 会等任务执行结束后停掉服务,而stopSelf()会立即结束。

6.Handler

  • 主线程创建
    • Handler uiHandler = new Handler();
    • Handler uiHandler = new Handler(Looper.getMainLooper());
  • 创建子线程Handler
    • Handler threadHandler = new Handler(mHandlerThread.getLooper());获取子线程Looper
  • 原理,根据Looper找到关联的消息队列
    image

子线程创建Handler,需要先进行 Looper.prepare()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
new Thread(){
@Override
public void run() {
Looper.prepare(); //获取当前线程的Looper,并prepare();
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Toast.makeText(MainActivity.this, "handler msg", Toast.LENGTH_SHORT).show();
}
};
handler.sendEmptyMessage(0);
Looper.loop(); //looper开始处理消息。
}
}.start();

7.继承Thread

1
2
3
4
5
6
7
8
9
10
TestThread testThread = new TestThread();
testThread.start();
class TestThread extends Thread {
@Override
public void run() {
super.run();
}
}

8.Runnable

1
2
3
4
5
6
7
8
9
TestRunnable run = new TestRunnable();
Thread tt = new Thread(run);
tt.start();
class TestRunnable implements Runnable{
@Override
public void run() {
}
}

9. AsyncTask 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
AsyncTestThread asyncTestThread = new AsyncTestThread();
asyncTestThread.execute();// 串行
asyncTestThread.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");//并行
class AsyncTestThread extends AsyncTask<String, String, String> {
// 1作用:执行 线程任务前的操作
@Override
protected void onPreExecute() {
Log.e("jfson", "pre 0");
super.onPreExecute();
}
// 2作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
@Override
protected String doInBackground(String... strings) {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
//进度条每次更新10%,执行中创建新线程处理onProgressUpdate()
publishProgress(String.valueOf(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.e("jfson", "doInBackground finish :");
return "下载完成!";
}
// 3作用:在主线程 显示线程任务执行的进度
@Override
protected void onProgressUpdate(String... values) {
Log.e("jfson", "onProgressUpdate :" + values[0]);
super.onProgressUpdate(values);
}
// 4作用:接收线程任务执行结果..UI线程
@Override
protected void onPostExecute(String s) {
Log.e("jfson", "onPostExecute :" + s);
super.onPostExecute(s);
}
// 5任务执行完成、UI线程
@Override
protected void onCancelled() {
Log.e("jfson", "onCancelled :");
super.onCancelled();
}
}

10.多线程的实现 or api 实现

线程池API分析

  • 1.创建
    • a.ThreadPoolExecutor.newFixedThreadPool();创建线程数量固定的线程池
    • b.ThreadPoolExecutor.newSingleThreadExecutor();线程数固定为1的线程池
    • c.ThreadPoolExecutor.newCachedThreadPool();会缓存的线程池,线程数量可以从0到Integer.MAX_VALUE,超时时间为1分钟。线程池用起来的效果是:如果有空闲线程,会复用线程;如果没有空闲线程,会新建线程;如果线程空闲超过1分钟,将会被回收。
    • d. ThreadPoolExecutor.newScheduledThreadPool();将会创建一个可定时执行任务的线程池。
  • 2.BlockingQueue
    • newCachedThreadPool的线程上限几乎等同于无限,但系统资源是有限的,任务的处理速度总有可能比不上任务的提交速度。因此,可以为ThreadPoolExecutor提供一个阻塞队列来保存因线程不足而等待的Runnable任务,这就是BlockingQueue。
  • 3.SynchronousQueue
    • newCachedThreadPool使用的SynchronousQueue十分有趣,看名称是个队列,但它却不能存储元素。要将一个任务放进队列,必须有另一个线程去接收这个任务,一个进就有一个出,队列不会存储任何东西。因此,SynchronousQueue是一种移交机制,不能算是队列。newCachedThreadPool生成的是一个没有上限的线程池,理论上提交多少任务都可以,使用SynchronousQueue作为等待队列正合适。
  • 4.饱和策略
    • 当有界的等待队列满了之后,就需要用到饱和策略去处理,ThreadPoolExecutor的饱和策略通过传入RejectedExecutionHandler来实现。如果没有为构造函数传入,将会使用默认的defaultHandler。
    • a.AbortPolicy是默认的实现,直接抛出一个RejectedExecutionException异常,让调用者自己处理。
    • b.DiscardPolicy的rejectedExecution直接是空方法,什么也不干。如果队列满了,后续的任务都抛弃掉。
    • c.DiscardOldestPolicy会将等待队列里最旧的任务踢走,让新任务得以执行。
    • d.CallerRunsPolicy,它既不抛弃新任务,也不抛弃旧任务,而是直接在当前线程运行这个任务。当前线程一般就是主线程啊,让主线程运行任务,说不定就阻塞了。如果不是想清楚了整套方案,还是少用这种策略为妙。
  • 5.线程池的执行
    • 线程池是由Worker类负责执行任务,Worker继承了AbstractQueuedSynchronizer,引出了Java并发框架的核心AQS。
    • worker在线程池里的四种可能(Worker在构造函数里采用ThreadFactory创建Thread,在run方法里调用了runWorker,看来是真正执行任务的地方。) worker在线程池里的四种可能
  • 6.线程池的关闭
    • shutdown:不能再提交任务,已经提交的任务可继续运行;
    • shutdownNow:不能再提交任务,已经提交但未执行的任务不能运行,在运行的任务可继续运行,但会被中断,返回已经提交但未执行的任务。
  • 7.Join、wait、notify、notifyAll、run()和start()
    • Join()将改线程优先级提升,执行完后才可以执行其他线程。底层是利用wait()实现,主线程先获得了t对象的锁,t执行完成后,主线程继续执行,其他线程开始执行。
    • 在 Java 中,可以通过配合调用 Object 对象的 wait() 方法和 notify()方法或 notifyAll() 方法来实现线程间的通信。在线程中调用 wait() 方法,将阻塞等待其他线程的通知(其他线程调用 notify() 方法或 notifyAll() 方法),在线程中调用 notify() 方法或 notifyAll() 方法,将通知其他线程从 wait() 方法处返回。
    • run()和start()
      • 1) start()
        用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
      • 2)run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。

10.手写一个线程池

  • 主要用 private BlockingQueue taskQueue=new LinkedBlockingDeque(); 存储任务Runnable。
  • //线程工作组,WorkerThread[] workThreads;存储初始化的线程
  • taskQueue.take();// 获取并移除第一个元素 没有则扔进
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public void execute(Runnable task) {
synchronized (taskQueue) {
taskQueue.add(task);
taskQueue.notifyAll(); //当调用execute()方法的时候,执行notiry(),只会唤醒线程池中的一个线程,注意与notoryAll()的区别
}
}
/*内部类 即一个线程池对象*/
private class WorkThread extends Thread{
//该工作线程是否有效,用来接收该工作线程
private boolean isRunnable=true;
/*
* 关键所在,如果任务队列不空,则求出任务执行,若任务队列为空,则等待
*/
@Override
public void run() {
//接收队列当中的任务对象 任务对象Runnable类型
Runnable r=null;
while(isRunnable){
//队列同步机制
synchronized(taskQueue){
while(isRunnable && taskQueue.isEmpty()){//队列为空
try {
taskQueue.wait(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(!taskQueue.isEmpty()){
try {
r=taskQueue.take();// 获取并移除第一个元素
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(r!=null){
r.run();//执行任务
}
executeTaskNumber++;
r=null;
}
}
}
public void stopWorker(){
isRunnable=false;
}
}

HttpURLConnection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static String get(IRequest request) {
InputStream inputStream = null;
HttpURLConnection httpURLConnection = null;
try {
URL url = new URL(buildGetUrl(request.getBaseUrl(), request.getParam(), request.getEncrypt()));
openUrlConnection(url,httpURLConnection);
normalSetting(httpURLConnection, Method.GET, request.getHeaders());
if (httpURLConnection == null) {
return null;
}
int responseCode = httpURLConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = httpURLConnection.getInputStream();
String contentEncoding = httpURLConnection.getContentEncoding();
InputStream stream = null;
try {
stream = wrapStream(contentEncoding, inputStream);
String data = convertStreamToString(stream);
return data;
} catch (IOException e) {
return "";
} finally {
closeQuietly(stream);
}
}
return null;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}


pv UV: