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 就会通过调度的方式,并发执行多个线程:
多线程的好处:多个线程互不影响,因为它们在不同的栈空间。
(完)