Java 中的线程
# Java 中的线程
接下来我们实操下,在 Java 中创建线程
# 主线程
先来说一个比较特殊的线程:主线程。
主线程是执行主方法(main)的线程。我们之前写的 Java 程序中只有一个线程,也叫单线程程序,执行从 main 方法开始(由 JVM 执行),从上到下依次执行,main 方法会进入到栈内存
JVM 会找操作系统开辟一条 main 方法通向 CPU 的执行路径,CPU 就可以通过这个路径来执行 main 方法,而这个路径有一个名字,叫 main(主)线程
我们新建一个 Person 类,有 name 属性,setter/getter,构造方法和 run 方法:
package chapter200Thread;
public class Person {
private String name;
public void run(){
// 定义循环,执行20次
for (int i = 0; i < 20; i++) {
System.out.println(name + " --> " + i);
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person() {
}
public Person(String name) {
this.name = name;
}
}
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
然后我们新建一个类用这个 Person:
package chapter200Thread;
public class Demo01MainThread {
public static void main(String[] args) {
Person p1 = new Person("小强");
p1.run();
Person p2 = new Person("旺财");
p2.run();
}
}
2
3
4
5
6
7
8
9
10
11
执行结果:各执行了 20 次循环。
缺陷:如果中途有异常,则后续的代码不能执行,例如我们加个异常:
package chapter200Thread;
public class Demo01MainThread {
public static void main(String[] args) {
Person p1 = new Person("小强");
p1.run();
int i = 0 / 0;
Person p2 = new Person("旺财");
p2.run();
}
}
2
3
4
5
6
7
8
9
10
11
12
运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at chapter200Thread.Demo01MainThread.main(Demo01MainThread.java:8)
2
我们可以创建两个线程,一个线程输出“小强”,一个线程输出“旺财”,这样即使有一个线程异常了,也不会影响另一个。
# Java 中的线程
Java 使用 java.lang.Thread 类代表线程,所有的线程对象都必须是 Thread 类或其子类的实例。
我们可以看官网文档 (opens new window)中,对 Thread 类的说明:
A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.
线程是程序中的执行线程,JVM 运行应用程序并发地运行多个执行线程。
Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon. When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon.
每个线程有个优先级,高优先级线程,会优先执行。每个线程都可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程是守护线程
....
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started. For example, a thread that computes primes larger than a stated value could be written as follows:
有两种方法创建线程,一种是将类声明为 Thread 的子类,该子类需重写 run 方法。接下来就可以分配并创建该子类的实例,例如,计算大于某一规定值的质数的线程可以写成:
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
2
3
4
5
6
7
8
9
10
11
The following code would then create a thread and start it running:
然后,下列代码会创建并启动一个线程:
PrimeThread p = new PrimeThread(143);
p.start();
2
The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started. The same example in this other style looks like the following:
创建线程的第二种方式是声明实现 Runnable 接口的类,然后实现 run 方法。然后就可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。采用这种方式的一个例子:
class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }1
2
3
4
5
6
7
8
9
10
11
12
The following code would then create a thread and start it running:
然后,下列代码会创建并启动一个线程:
PrimeRun p = new PrimeRun(143); new Thread(p).start();1
2
看完文档后,我们来实现下。
# 创建线程方式之一
我们先用第一种方式:创建 Thread 类的子类
实现步骤:
- 创建一个 Thread 类的子类
- 在 Thread 类的子类中重写 Thread 类中的 run 方法,设置线程任务
- 创建 Thread 类的子类对象
- 调用 Thread 类中的方法 start 方法,该方法会开启新的线程,并执行 run 方法
关于 start 方法的文档说明 (opens new window):
public void start()Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread. The result is that two threads are running concurrently: the current thread (which returns from the call to the start method) and the other thread (which executes its run method).
It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
结果是两个线程并发地运行;当前线程(main 线程)和另一个线程(创建的新线程,会执行其 run 方法)。
多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
Java 程序属于抢占式调度,哪个线程的优先级高,就会优先执行;同一个优先级,则随机选择一个执行。
我们创建一个类:
package chapter200Thread;
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++){
System.out.println("run: " + i);
}
}
}
2
3
4
5
6
7
8
9
10
开启多线程:
package chapter200Thread;
public class Demo02ThreadSubclass {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // 启动线程
// 主线程继续执行
for (int i = 0; i < 20; i++) {
System.out.println("main: " + i);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
然后我们执行 main 方法,结果:
main: 0
run: 0
main: 1
run: 1
main: 2
run: 2
main: 3
run: 3
main: 4
main: 5
main: 6
run: 4
........
2
3
4
5
6
7
8
9
10
11
12
13
可以看到是交替输出的,也就是两个线程是交替执行的。
# 多线程内存图解
首先,我们写了 2 个方法:main 方法和 run 方法

当我们执行 main 方法的时候,会将 main 方法加载到内存中,我们可以称之为栈:

然后我们会创建 MyThread 对象,该对象会在内存中一个叫“堆”的地方,并且该对象有自己的内存地址:

然后当我们执行 run 方法的时候,如果是普通方法,此时就是单线程程序,该方法也会压栈执行,先执行 run 方法,再执行 main 方法后面的代码:

而我们是多线程,所以,当我们调用 start 方法的时候,会创建一个栈空间,所以我们的 run 方法是在新的栈空间执行:

同理,如果我们创建多个 MyThread 类,并且调用 start 方法,就会创建多个栈空间,并执行 run 方法,CPU 就会通过调度的方式,并发执行多个线程:

多线程的好处:多个线程互不影响,因为它们在不同的栈空间。
(完)