Java多线程-Thread类的run方法
一、背景
- 在Java中,要使用多线程,很显然先要创建线程。
- Java将线程抽象为Thread类。创建线程的第一步:
new Thread()
- 创建好Thread对象后,接下来,就是要启动线程,并执行线程需要做的事情:Thread对象.start();
(1)首先会启动线程;
(2)然后调用run方法();
二、研究Thread类的start()源码
1、源码(比较短,贴一下)
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
1.1 重点:start0();
- 源码
private native void start0();
- 是一个本地方法。也很好理解,Java的线程是交由操作系统来管理的。
start0()方法是Java中的一个
本地方法
,也就是说它是用其他语言(通常是C或C++)实现的。
其主要逻辑是创建一个新的系统级线程,并在新线程上执行run()方法。
Java中的线程是基于操作系统的原生线程实现的,而start0()方法主要就是与操作系统进行交互,创建和启动新的系统级线程。
具体来说,start0()方法的执行逻辑一般包括以下步骤:
(1)申请系统资源创建一个新的系统级线程。
(2)将Java线程对象与新创建的系统级线程进行关联。
(3)设置线程的优先级和栈大小等线程属性。
(3)在新的系统级线程上调用Java线程对象的run()方法。
(4)如果run()方法执行结束或发生异常,清理系统级线程资源。
以上执行逻辑可能因不同的JVM实现和不同的操作系统有所不同,具体实现代码可以在JVM的源代码中找到。
三、研究Thread类的run()源码
1、源码(很关键,必须贴)
@Override
public void run() {
if (target != null) {
target.run();
}
}
- Thread类:
public class Thread implements Runnable
Runnable的源码:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
既然Thread类实现了Runnable接口,就要覆写run方法。
- target是什么?
public class Thread implements Runnable {
......
private Runnable target;
......
}
- 用户可以通过这个构造方法传入target:
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
四、创建线程的2种方式
通过上面的分析,要让线程执行某段逻辑的关键是,实现run方法。
有2种方式:
(1)继承Thread,并覆写run方法
(2)传入一个实现了run方法的Runnable接口的实现类
1、继承Thread,并覆写run方法
- 示例
public class PrintEvenThread extends Thread {
@Override
public void run() {
// 遍历100以内的偶数
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class PrintOddThread extends Thread {
@Override
public void run() {
// 遍历100以内的奇数
for (int i = 0; i < 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class PrintThreadTest {
@Test
public void testTwoPrintThread() {
PrintEvenThread printEvenThread = new PrintEvenThread();
PrintOddThread printOddThread = new PrintOddThread();
printEvenThread.start();
printOddThread.start();
}
}
- 还可以:匿名类的匿名对象的方式
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}.start();
2、传入一个实现了run方法的Runnable接口的实现类
- 示例 (匿名类的匿名对象)
public class LearnCreateThreadMain3 {
public static void main(String[] args) {
new Thread(new PrintEven()).start();
}
}
class PrintEven implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
}
}
- 还可以:匿名类的匿名对象的方式
new Thread(
new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0) {
System.out.println(
Thread.currentThread().getName() + ":" + i);
}
}
}
})
.start();
五、到底执行哪个run方法?
- 示例
public class LearnCreateThreadMain4 {
public static void main(String[] args) {
Hi hi = new Hi();
new Thread(hi) {
@Override
public void run() {
System.out.println("hello~");
}
}.start();
}
}
class Hi implements Runnable {
@Override
public void run() {
System.out.println("Hi~");
}
}
- 看懂了“三、研究Thread类的run()源码”,很容易回答:执行的是Thread子类对象的run方法。也就是输出:
hello~
。 - 如果子类没有覆写Thread的run()方法,那么执行的代码是:
@Override
public void run() {
if (target != null) {
target.run();
}
}
- 一旦子类覆写了,那执行的就是子类的run()方法了。