Android多线程和异步任务

1,多线程的意义

a,为什么要用多线程

  1. 提高用户体验或者避免ANR*****
  2. 异步:不需要同步阻塞去等待返回结果,可以通过多线程来实现异步
  3. 多任务:多线程下载

b,为什么通过多线程可以提高用户避免ANR

  1. 什么是ANR:Application Not Responding程序未响应
  2. 深入了解:他其实就是当用户进行了一系列的操作之后,在规定的时间内无法得到响应,系统就会弹出一个ANR对话框,由用户决定是继续等待还是强制结束应用程序,应该所有人都经历过这个场景。
  3. 事件处理原则:所有的耗时操作都放在其他线程中去处理

c,如何实现多线程之间的通讯

  1. Handler
  2. AsyncTask

d,利用线程池提高性能

不需要重复的创建和销毁对象(重复创建和销毁对象很浪费资源),通过线程池去管理对象的生命周期。

2,多线程的创建

这两个的实例就不在这里复现了,比较简单而且以前也写过,地址在这儿 ——-》https://daisy-1999.site/2021/07/06/Android-%E5%AD%A6%E4%B9%A0%E4%B9%8B%E6%97%85/%E8%BF%9B%E7%A8%8B%E9%97%B4%E7%9A%84%E9%80%9A%E4%BF%A1%E4%B9%8B%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%BC%80%E5%8F%91/

a,继承Thread类实现多线程

b,实现Runnable接口方式实现多线程

c,多线程共享同一个变量,操纵同一个Runnable接口(实例)

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.example.study.thread;

import android.util.Log;

public class SaleTicket implements Runnable{
private int ticket = 20;
private static final String TAG = "SaleTicket";
@Override
public void run() {

while(true){
//线程同步必须使用同步锁
synchronized (this){
if(ticket > 0){
Log.e(TAG, Thread.currentThread().getName() + "售出了第" + (20-ticket+1) + "张票" );
ticket--;
} else {
break;
}
}

}
}
}

btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
testSaleTicket();
}
});

public void testSaleTicket(){
SaleTicket st = new SaleTicket();
Thread t1 = new Thread(st, "A代理");
Thread t2 = new Thread(st, "B代理");
Thread t3 = new Thread(st, "C代理");
Thread t4 = new Thread(st, "D代理");
t1.start();
t2.start();
t3.start();
t4.start();
}


打印结果
E/SaleTicket: A代理售出了第1张票
E/SaleTicket: C代理售出了第2张票
E/SaleTicket: D代理售出了第3张票
E/SaleTicket: B代理售出了第4张票
E/SaleTicket: A代理售出了第5张票
E/SaleTicket: C代理售出了第6张票
E/SaleTicket: D代理售出了第7张票
E/SaleTicket: B代理售出了第8张票
E/SaleTicket: C代理售出了第9张票
E/SaleTicket: A代理售出了第10张票
E/SaleTicket: D代理售出了第11张票
E/SaleTicket: B代理售出了第12张票
E/SaleTicket: C代理售出了第13张票
E/SaleTicket: A代理售出了第14张票
E/SaleTicket: D代理售出了第15张票
E/SaleTicket: B代理售出了第16张票
E/SaleTicket: C代理售出了第17张票
E/SaleTicket: B代理售出了第18张票
E/SaleTicket: D代理售出了第19张票
E/SaleTicket: A代理售出了第20张票

3,线程池的应用

a,new Thread的弊端

  1. 每次new Thread新建对象性能差
  2. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,很大可能占用过多的资源导致死机
  3. 缺乏更多的功能,如定时执行,定期执行,线程终端

b,Java线程池

好处
  1. 重用存在的线程,减少对象的创建、消亡的开销,性能好
  2. 可以有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多的资源竞争,避免堵塞
  3. 提供定时执行、定期执行、单线程、并发数控制等功能
1,newCachedThreadPool

定义

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可以灵活地回收空闲线程,若没有回收的,就会创建新线程

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
public void testCache(){
ExecutorService cachethreadPool = Executors.newCachedThreadPool();
for(int i = 0; i < 10; i++){
final int index = i;
cachethreadPool.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + "------->" + index);
}
});
}
}

changeText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
testCache();
}
});

D/ThreadActivity: pool-2-thread-3------->2
D/ThreadActivity: pool-2-thread-2------->1
D/ThreadActivity: pool-2-thread-1------->0
pool-2-thread-1------->4
pool-2-thread-1------->7
D/ThreadActivity: pool-2-thread-2------->5
D/ThreadActivity: pool-2-thread-3------->6
D/ThreadActivity: pool-2-thread-4------->3
pool-2-thread-4------->9
D/ThreadActivity: pool-2-thread-5------->8

由此可以看出并发的时候一共有五个线程

展示灵活回收线程的特性,工作完成之后休息两秒,然后又继续工作

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
public void testCache(){
ExecutorService cachethreadPool = Executors.newCachedThreadPool();
for(int i = 0; i < 10; i++){
final int index = i;
//添加了一个休眠两秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachethreadPool.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + "------->" + index);
}
});
}
}

D/ThreadActivity: pool-3-thread-1------->0
D/ThreadActivity: pool-3-thread-1------->1
D/ThreadActivity: pool-3-thread-1------->2
D/ThreadActivity: pool-3-thread-1------->3
D/ThreadActivity: pool-3-thread-1------->4
D/ThreadActivity: pool-3-thread-1------->5
D/ThreadActivity: pool-3-thread-1------->6
D/ThreadActivity: pool-3-thread-1------->7
D/ThreadActivity: pool-3-thread-1------->8
D/ThreadActivity: pool-3-thread-1------->9
2,newFixedThreadPool

定义

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

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
public void testFixed(){
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for(int i = 0; i < 10; i++ ){
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName()+ "------->" + index);
}
});

}
}

最大并发执行的线程数就是3
D/ThreadActivity: pool-2-thread-1------->0
pool-2-thread-1------->3
pool-2-thread-1------->4
pool-2-thread-1------->5
pool-2-thread-1------->6
pool-2-thread-1------->7
pool-2-thread-1------->8
D/ThreadActivity: pool-2-thread-3------->2
D/ThreadActivity: pool-2-thread-1------->9
D/ThreadActivity: pool-2-thread-2------->1

当每一个任务执行完成后休息两秒,再继续工作

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
public void testFixed(){
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for(int i = 0; i < 10; i++ ){
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName()+ "------->" + index);

try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

}
}

//打印结果的时候是三个三个的出现,这就能体现出来他的最大并发数是3

D/ThreadActivity: pool-2-thread-1------->0
D/ThreadActivity: pool-2-thread-2------->1
D/ThreadActivity: pool-2-thread-3------->2


D/ThreadActivity: pool-2-thread-2------->3
D/ThreadActivity: pool-2-thread-3------->4
D/ThreadActivity: pool-2-thread-1------->5


D/ThreadActivity: pool-2-thread-2------->6
D/ThreadActivity: pool-2-thread-1------->7
D/ThreadActivity: pool-2-thread-3------->8


D/ThreadActivity: pool-2-thread-1------->9
3,newSingleThreadExecutor

定义

newSingleThreadExecutor创建一个单线程化的线程池,他只会用唯一的工作线程来执行任务,保证任务按照指定顺序执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void testSingle(){
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
for(int i = 0; i < 10; i++){
final int index = i;
singleThreadPool.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + "------>" + index);
}
});
}
}

D/ThreadActivity: pool-2-thread-1------>0
pool-2-thread-1------>1
pool-2-thread-1------>2
pool-2-thread-1------>3
pool-2-thread-1------>4
pool-2-thread-1------>5
pool-2-thread-1------>6
pool-2-thread-1------>7
pool-2-thread-1------>8
pool-2-thread-1------>9

当休眠两秒之后,再继续工作

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
public void testSingle(){
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
for(int i = 0; i < 10; i++){
final int index = i;
singleThreadPool.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + "------>" + index);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}


D/ThreadActivity: pool-2-thread-1------>0
D/ThreadActivity: pool-2-thread-1------>1
D/ThreadActivity: pool-2-thread-1------>2
D/ThreadActivity: pool-2-thread-1------>3
D/ThreadActivity: pool-2-thread-1------>4
D/ThreadActivity: pool-2-thread-1------>5
D/ThreadActivity: pool-2-thread-1------>6
D/ThreadActivity: pool-2-thread-1------>7
D/ThreadActivity: pool-2-thread-1------>8
D/ThreadActivity: pool-2-thread-1------>9
4,newScheduledThreadPool

定义

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void testScheduled(){
Log.e(TAG, "testScheduled");
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
Log.e(TAG, "delay 3 second");
}
}, 3, TimeUnit.SECONDS);
}

E/ThreadActivity: testScheduled
//延迟了三秒再执行的
E/ThreadActivity: delay 3 second

周期性执行任务,定时两秒以后执行,每隔三秒执行一次

1
2
3
4
5
6
7
8
9
10
public void testScheduled(){
Log.e(TAG, "testScheduled");
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.e(TAG, "delay 2 second, and excute every 3 seconds ");
}
}, 2, 3, TimeUnit.SECONDS);
}

4,异步消息处理机制

Handler

a,分析
  1. Handeler

    Handler在android中负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯

  2. Looper

    Looper负责管理线程的消息队列和消息循环

  3. Message

    Message是线程间通讯的消息载体,举个例子来说就是,两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何想要传递的消息

  4. MessageQueue

    MessageQueue是消息队列,先进先出,它的作用就是保存有待线程处理的消息

他们四者之间的关系就是,在其他线程中调用Handler.sendMessage(“参数就是Message对象 ”)方法,将需要Main线程处理的事件添加到Main线程的MessageQueue中,Main线程通过MainLopper从消息队列中取出Handler发送过来的消息时,会回调Handler的handlerMessage()方法

b,用法

sendEmptyMessage(int)

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
public class ThreadActivity extends AppCompatActivity {

private Button changeText;
private TextView showText;
private static final String TAG = "ThreadActivity";

private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
Toast.makeText(getApplicationContext(), "hellp", Toast.LENGTH_LONG).show();
break;
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);

changeText = (Button) findViewById(R.id.change_text);
showText = (TextView) findViewById(R.id.show_text);
changeText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(1);
//changeText.setText("changed");

}
}).start();
}
});

}
}

sendMessage(Message)

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
50
51
public class ThreadActivity extends AppCompatActivity {

private Button changeText;
private TextView showText;
private static final String TAG = "ThreadActivity";

private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
//Toast.makeText(getApplicationContext(), "hellp", Toast.LENGTH_LONG).show();
String string = (String) msg.obj;
Toast.makeText(getApplicationContext(), string, Toast.LENGTH_LONG).show();
break;
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);

changeText = (Button) findViewById(R.id.change_text);
showText = (TextView) findViewById(R.id.show_text);
changeText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
//模拟网络请求
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}

String getData = "showMessage";
Message msg = new Message();
msg.what = 1;
msg.obj = getData;
handler.sendMessage(msg);
}
}).start();
}
});

}
}

5,异步任务

AsyncTask在之前也写过,就不再这里重复了,https://daisy-1999.site/2021/07/06/Android-%E5%AD%A6%E4%B9%A0%E4%B9%8B%E6%97%85/%E8%BF%9B%E7%A8%8B%E9%97%B4%E7%9A%84%E9%80%9A%E4%BF%A1%E4%B9%8B%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%BC%80%E5%8F%91/