一般来说java实现多线程都会说继承Thread并重写run方法或者实现Runnable接口,但是为什么会是这两种方式?学习还是要知其所以然,我们分析一下源代码,然后写几个简单实现的例子。
Thread和Runnable
首先查看Thread源代码只有一个启动方法start(),研究start()方法的执行过程,发现真正的执行方法体是Thread.start0()调用的Thread.run();
方法注释中包含以下信息:
1.此线程从此开始执行
2.将调用此线程的run()方法
3.当前线程的start方法和其他线程的run方法同时调用时两个线程会同时运行
4.多次启动线程是不合法的,特别是完成后不能重新启动
。
Thread.start()方法源码:
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
继续分析run()方法:
1.如果此线程是一个Runnable的运行对象,将会调用Runnable对象的run()方法
2.Thread的子类应该重写此方法
由代码可知,如果Runnable为null且子类没有重写run()方法,此run()是个空方法,Thread不做任何操作直接返回
Thread.run()源码:
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
查看Runnable赋值过程,发现只有一个构造方法传入了Runnable参数,并通过init()方法赋值给target属性,target仅有两处使用:
1.在start0()方法中调用了target的run()方法
2.在exit()方法中设置target为null
Thread的一个构造函数:
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this classes {@code run} method does
* nothing.
*/
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
//作者注:init方法中进行了赋值:this.target = target;
}
查看java.lang.Runnable源代码,它只有一个抽象方法run(),void没有返回值并且不能抛出异常;
public interface Runnable {
public abstract void run();
}
简单总结
通过分析源代码可以Java线程最终要执行的方法体就是run。
@Override
public void run() {
if (target != null) {
target.run();
}
}
所以新建一个自定义线程有两种方法:
1.继承Tread并重写run()方法
2.在构造Thread的时候把要执行的方法用Runnable接口的实现作为参数传入,最终Runnable.run()在Thread.run()中调用执行。
简单实现
简单介绍一下Java多线程的几种基本实现方式,不过Thread和Runnable都无法获取执行结果,需要执行结果可以使用Callable。
新建一个类继承Thread类或者实现Runnable接口,
使用匿名内部类
Lambda表达式
继承Thread类的 MyThread
package com.example.demo.thread;
public class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i < 5; i++) {
try {
sleep((int) Math.ceil(Math.random() * 100) * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "运行 : " + i);
}
}
}
实现Runnable接口的 MyThreadRunnable
package com.example.demo.thread;
public class MyThreadRunnable implements Runnable{
private String name;
public MyThreadRunnable(String name) {
this.name=name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep((int) Math.ceil(Math.random() * 100) * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "运行 : " + i);
}
}
}
测试类:
package com.example.demo.thread;
public class TestThread {
public static void main(String[] args) {
/**
* 原始方法实现thread
* 新建一个类MyThread继承Thread
* 并重写run方法
*/
MyThread myThread1 = new MyThread("A");
MyThread myThread2 = new MyThread("B");
myThread1.start();
myThread2.start();
/**
* 原始方式通过实现Runnable接口
*
*/
new Thread(new MyThreadRunnable("C")).start();
new Thread(new MyThreadRunnable("D")).start();
final String E="E";
final String F="F";
final String G="G";
final String H="H";
/**
* 匿名内部类方式实现
* 代码内添加下面代码
*/
new Thread() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
sleep((int) Math.ceil(Math.random() * 100) * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(E+"运行 : " + i);
}
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
sleep((int) Math.ceil(Math.random() * 100) * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(F+"运行 : " + i);
}
}
}.start();
/**
* java8+新特特性
* Lambda 表达式
*/
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep((int) Math.ceil(Math.random() * 100) * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(G+"运行 : " + i);
}
}).start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep((int) Math.ceil(Math.random() * 100) * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(H+"运行 : " + i);
}
}).start();
}
}
测试方法运行结果:
A运行 : 0
C运行 : 0
H运行 : 0
D运行 : 0
F运行 : 0
F运行 : 1
C运行 : 1
B运行 : 0
C运行 : 2
F运行 : 2
F运行 : 3
C运行 : 3
A运行 : 1
G运行 : 0
E运行 : 0
C运行 : 4
B运行 : 1
G运行 : 1
D运行 : 1
G运行 : 2
H运行 : 1
D运行 : 2
E运行 : 1
B运行 : 2
B运行 : 3
D运行 : 3
E运行 : 2
A运行 : 2
F运行 : 4
G运行 : 3
H运行 : 2
D运行 : 4
G运行 : 4
B运行 : 4
A运行 : 3
E运行 : 3
H运行 : 3
A运行 : 4
E运行 : 4
H运行 : 4
使用线程池
强烈建议使用线程池获取线程,不建议自己显示建立线程
测试方法:
public static void testThreadPool(){
ExecutorService executor = Executors.newCachedThreadPool();
for(int i=0;i<10;i++){
final int num=i;
executor.execute(() -> {
try {
Thread.sleep((int) Math.ceil(Math.random() * 100) * 100);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("线程"+num+" 执行了");
});
}
}
执行结果:
执行结果:
线程8 执行了
线程1 执行了
线程5 执行了
线程6 执行了
线程9 执行了
线程0 执行了
线程3 执行了
线程4 执行了
线程7 执行了
线程2 执行了