java 多线程

关于 java 多线程的一些简单入门知识。

线程

线程简介

线程与进程类似,但是是比进程更小的执行单位,一个进程在其执行过程中可以产生多个线程。同一个进程中每个线程共享进程的资源。因此在切换线程时耗费的资源要比切换进程时少得多。

多线程

多线程是指几乎同时去处理多个线程。使用多线程可以把占据时间长的程序中的任务放到后台去处理;程序运行效率可能会提高;在等待任务上(比如输入、文件读取、网络收发数据等)多线程就能够发挥更大的作用。

线程状态

线程与进程类似,主要有初始、就绪、运行、阻塞、结束等状态。具体的状态转换关系参考下图:

图片来自网络,侵删

初始状态:新创建了一个线程。

就绪状态:创建线程对象后,其他线程调用了该线程对象的 start() 方法。该状态的线程位于可运行线程池中,变得可运行,等待获取 CPU 的使用权。

运行状态:就绪状态的线程获取了 CPU,执行程序代码。

阻塞状态:阻塞状态是由于某些原因线程放弃 CPU 使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

结束状态:线程执行完毕或者因异常退出了 run() 方法,该线程结束生命周期。

创建多线程

继承 java.lang.Thread 类创建线程

通过继承 Thread 类创建线程是比较常用的方法。格式如下:

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
class Thread1 extends Thread{
private String name;

public Thread1(String name) {
this.name = name;
}

public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}
public class Main {

public static void main(String[] args) {
Thread1 mTh1 = new Thread1("A");
Thread1 mTh2 = new Thread1("B");
// 该 start() 方法调用于 Thread,利用该方法,可以执行线程中的 run() 方法。
mTh1.start();
mTh2.start();
}

}
/*
output:
B运行 : 0
A运行 : 0
B运行 : 1
A运行 : 1
A运行 : 2
B运行 : 2
B运行 : 3
B运行 : 4
A运行 : 3
A运行 : 4
*/

从输出可以看出,线程的运行状态并非按照程序的声明顺序。原因在于调用 start() 方法并非立即执行该线程,而是将该线程从初始状态转变为就绪状态,什么时候运行取决于操作系统。因此可以知道输出结果是乱序的为正常现象。

由于多线程是乱序的,因此在设计多线程时,需要判断这些线程之间是否是乱序执行的(无关联)。比如做饭时,炒菜,蒸饭,吃饭;炒菜和和蒸饭可以乱序执行(多线程),并没有绝对的先后顺序。然而吃饭必须在前两者之后,因此不能与前两者共同执行。

实现 java.lang.Runnable 接口创建线程

不同于 Thread 实现方法,利用 Runnable 接口创建线程时,run() 方法必须被实现,否则编译器将会报错。Thread 类实际上也是在 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
package com.example.test;


class Thread2 implements Runnable{
private String name;

public Thread2(String name) {
this.name = name;
}
//run() 方法必须被重写。
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
Thread.sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}

}
public class Test {

public static void main(String[] args) {
new Thread(new Thread2("C")).start();
new Thread(new Thread2("D")).start();
}

}

/*
output:
C运行 : 0
D运行 : 0
C运行 : 1
D运行 : 1
D运行 : 2
D运行 : 3
C运行 : 2
D运行 : 4
C运行 : 3
C运行 : 4

*/

通过代码可以看出,首先需要 new 一个线程对象,然后利用 Thread 类的 start() 方法去运行多线程代码。事实上,所有的多线程代码均是通过 Thread 类的 start() 方法来运行的,因此不管是继承 Thread 类还是实现 Runnable 接口最终都是通过 Thread 对象的 API 来控制线程的。

线程常用方法

sleep(long millis)

使当前正在运行的线程休眠一定时间。

join()

指定主线程结束晚于子线程。用法为:

1
2
3
4
// 在 start() 方法后调用 join() 方法。
Thread t = new AThread();
t.start();
t.join();

yield()

暂停当前正在执行的线程对象,并执行其他线程。

yield() 的机制为:将当前线程对象从运行状态转变为就绪状态,这样就给了其他线程进入运行状态的机会,然而这并不时一定能够改变多线程的运行顺序,因为状态的转换取决于操作系统的调用算法。

setPriority()

设置线程的优先级(1 - 10),初始每个线程优先级均为 5 。

感谢您的支持
-------------本文结束感谢您的阅读-------------