多线程的实现方式:
多线程的实现方式,目前有4中: Thread、Runnable、Callable、线程池【本文不介绍】
- Thread类 :通过继承Thread类,并重写其中的Run()方法,调用Thread.start() 方法启动线程
- Runnable接口:实现Runnable接口,并且完成Run()方法,仍需借用Thread.start()方法启动线程
- Callable接口: 通过实现接口,重写call()方法【有返回值】,通过 FutureTask装饰,最终也是需要Thread.start()方法启动线程
代码示例:
- 继承Thread :
public class ThreadTest extends Thread{
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.start();
}
@Override
public void run() {
System.out.println("I am Thread! ");
}
}
运行结果:
I am Thread!
- 实现Runnable 接口 :
public class RunnableTest implements Runnable {
public static void main(String[] args) {
RunnableTest runnableTest = new RunnableTest();
new Thread(runnableTest).start();
}
@Override
public void run() {
System.out.println("I am Runnable");
}
}
运行结果:
I am Runnable
- Callable接口实现多线程需要借助 FutureTask 类:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest implements Callable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTest callableTest = new CallableTest();
FutureTask<String> task = new FutureTask<String>(callableTest);
new Thread(task).start();
System.out.println(task.get());
}
@Override
public Object call() throws Exception {
return "I am CallableTest !";
}
}
运行结果:
I am CallableTest!
源码解析:
关于Thread类 和 Runnable接口,我们可以看到:
- Runnable 中 :
public interface Runnable {
// 定义了Run()方法,执行入口,业务逻辑均在此方法中实现
public abstract void run();
- 在Thread中:
public class Thread implements Runnable {
// Runnable的实现类, 上面的实例代码 runnableTest 对象
private Runnable target;
// 线程启动方法
public synchronized void start() {
// 状态校验 0:NEW 新建状态
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 添加进线程组 -main 方法中, 后续调用
group.add(this);
boolean started = false;
try {
//调用native方法执行线程run方法
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();
// 当多线程的实现方式实现Runnable 接口时,通过 thread.run() 方法调用 runnableTest.run()方法
public void run() {
if (target != null) {
target.run();
}
}
// 省略代码
}
- 我们可以看到不管是Thread 还是 Runnable 线程的真正实现都是有Thread.start() 方法启用的。
再调用 原生start0() , 回调到 run() 方法中【Runnable 通过thread.run() 方法调用 target.run(),实现我们的业务逻辑】 - Thread 和 Runnable 的区别就在于,Java 是单继承 和多实现,使用Thread 类 难免会对我们的业务扩展产生影响。
- 面试中还有一个常问的问题:如果一个线程多次Start 后的结果 , 答案就在于 if (threadStatus != 0) throw new IllegalThreadStateException() 这两代码,每次启动时都会判断 当前 thread 的运行状态,上次的start 会使得 threadStatus != 0 继而抛出异常。
关于FutureTask类 和 Callable接口实现的多线程:
- Callable 接口
public interface Callable<V> {
// 计算结果,如果无法执行,则引发异常
V call() throws Exception;
}
- FutureTask 继承关系:
FutureTask 部分源码:
public class FutureTask<V> implements RunnableFuture<V> {
// 实现 Runnable run() 方法
public void run() {
// 判断当前线程的状态
if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行 Call 方法,并获得返回结果
result = c.call();
ran = true;
} catch (Throwable ex) {
// call 方法抛出异常
result = null;
ran = false;
setException(ex);
}
// 执行成功将 返回结果放到 outcome 中,在通过get()获得
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
// 等待线程结束,获得线程的返回值 outcome
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
// 等待线程执行结束,或者执行异常
s = awaitDone(false, 0L);
return report(s);
}
// 等待线程执行结束,或者执行异常 ,并返回线程的状态
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
// 自旋等待线程执行结束
for (;;) {
// 线程被中断
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
// 执行结束返回
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
}
- 结合 Callable 和 FutureTask 源码不难发现, FutureTask 本身是实现了Runnable 接口,已经帮我们实现了多线程功能,我们定义的 CallableTest 作为一个 变量 赋值 FutureTask#callable 参数中,
- 在执行 Thread#start() 方法中,会调用 FutureTask#run() 方法,再到我们的 callable.call() 方法,进入到我们的业务,并把返回值赋值给 outCome。
- 当我们通过 task.get() 回去返回值时,通过等待 线程执行结束,获得 call() 的返回值并返回。
- 总而言之, FutureTask 是个已经写好了的线程对象,我仅需吧 Callable交给他执行,即可