线程的创建主要有三种方式
Thread直接创建
Thread类中有run()
方法,每次线程调用start()
时,线程启动并开始执行run()
方法。因此可以通过重写Thread类中的run()
方法来实现线程。
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("t1 run...");
}
};
thread.setName("t1");
thread.start();
System.out.println("main run...");
Runnable接口继承实现线程
Runnable runnable = () -> System.out.println("t1 run...");
new Thread(runnable, "t1").start();
System.out.println("main run....");
Runnable接口实现线程源码查看
首先,根据上述代码可以看到,在main()
方法中通过向Thread
类传入继承了Runnable
接口的类来实现线程,然后根据这个,找到在Thread
类中运行的代码
public Thread(Runnable target, String name) {
this(null, target, name, 0);
}
通过Thread
类中的源码发现,在该构造方法中调用了另一个构造方法,其中Runnable对象被传入到另一个构造方法的第二个参数的位置。然后追踪该被调用的构造方法,如下:
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
this(group, target, name, stackSize, null, true);
}
依然可以看到该构造方法又调用了另一个构造方法,而Runnable对象依然在被调用的构造方法的第二个参数位置。继续追踪被调用的构造方法,如下:
private Thread(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security manager doesn't have a strong opinion
on the matter, use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(
SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
this.tid = nextThreadID();
}
在上述代码的 第53行可以看到这样一行代码:this.target = target;
,由此可知,该构造方法中将Runnable的对象赋给了类中的属性。然后通过搜索Thread
类中的target
属性,发现了Thread
类中的run()
方法(其中线程创建时,当Thread
类的start()
方法调用后,会自动执行Thread
类中的run()
方法),而run()
方法的源码如下:
@Override
public void run() {
if (target != null) {
target.run();
}
}
通过上述代码可以看出,在Thread
类中的run()
方法中,首先判断target
是否存在,即Runnable
对象是否存在,如果存在,则调用Runnable
对象的run()
方法,而Runnable
接口的源码如下:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
因此,通过Runnable
接口创建线程时,需要实现Runnable
接口中的Run()
方法。虽然在线程创建的时候执行的是Thread
类中的run()
方法,但实际调用的还是Runnable
接口实现的run()
方法。
FutureTask类实现
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("t1 run ....");
return 100;
}
});
new Thread(task, "t1s").start();
int num = task.get();
System.out.println("main run" + num);
注意:
- 其中
FutureTask
类实现的线程方式是可以获取该线程的返回值的,获取方式如上代码通过task.get()
方式。如果FutureTask
创建的线程需要获取返回值,但线程未运行完,即线程还未返回值时,以上述代码为例,其中main
线程会在获取返回值的位置,即上述代码中的task.get()
处等待,当main
线程获取到返回值时,才继续向下运行。 - 其中
FutureTask
方式创建的线程有返回值,而Thread
和Runnable
方式创建的线程没有返回值。
FutureTask线程实现源码查看
同样,线程的实现都需要调用Thread
类,根据这个追踪Thread
类的构造函数。源码如下:
public Thread(Runnable target, String name) {
this(null, target, name, 0);
}
看到这的时候发现通过FutureTask
实现线程的方式与通过Runnable
接口实现线程的方式在Thread
类中的调用方式相同。但在上述Thread
类的构造方法中传入的是Runnable
接口实现的对象,而在main
方法中又可以看出在Thread
类中传入的是FutureTask
类。
FutureTask
类与Runnable
接口是什么关系呢?根据这个疑问查看FutureTask
类的源码:
public class FutureTask<V> implements RunnableFuture<V> {
// ......
}
根据上述代码,可以看到FutureTask
类并没有实现Runnable
接口,但实现了RunnableFuture
接口。(猜想:RunableFuture
接口继承了Runnable
接口),根据这个,再查看RunnableFuture
接口的源码, 如下:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
根据上面的代码是可以确认RunnableFuture
接口是继承Runnable
接口的。
通过上面的分析,可以知道为什么FutureTask
类的实例可以传入到Thread
类的实例中。但在通过FutureTask
类实现的线程中,实现了Callable
接口并通过实现call()
方法来实现该线程的逻辑,这是什么原因?FutureTask
类实现的线程又是如何返回值的。
通过之前的源码分析,可以知道,Thread
中的run()
方法调用的实际上是Runnable
接口的实现类的run()
方法。同时FutureTask
类实现了继承Runnable
接口的RunnableFutrue
接口,那么FutureTask
必然实现run()
方法,接下来查看FutureTask
类中的run()
方法实现,源码如下:
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
通过上面代码的第6行:Callable<V> c = callable;
可以看到将FutureTask
类中的属性callable
赋给了c
,并且在11行调用了call()
方法,并返回了结果给result
,第19行又执行了set(result)
代码。由此产生以下几个疑问:
FutureTask
类中的callable
属性的值或对象的实例是如何传进来的?(联想main()
方法中线程实现时在FuturtTask
类内传入了实现的Callable
接口的实例)。- 在
FutureTask
中run()
方法并没有返回值,那通过FutureTask
类实现的线程又是如何返回值的?(联想main()
方法中获取线程的返回值是通过FutureTask
类的实例调用get()
方法)
首先解决第一个疑问,callable
属性是如何获得值的。在main()
方法中传入了Callable
接口的实例,根据这个查看FutureTask
的源码,如下:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
如代码所示,FutureTask
类直接通过构造方法将Callable
类的实例传给callable
属性。
解决第二个问题,如何将线程的值返回。在FutureTask
类中的run()
方法有三行代码Callable<V> c = callable;
,result = c.call();
,set(result);
,再根据main()
方法获取线程的返回值是通过get()
方法,可以看出result
就是返回值,call()
是Callable
接口的方法。其中Callable
接口源码如下:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
set(result)
代码是将返回值结果存储到了FutureTask
的属性中,可以追踪FutureTask
类中set()
方法的源码,如下:
protected void set(V v) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v;
STATE.setRelease(this, NORMAL); // final state
finishCompletion();
}
}
可以看到,set()
方法中将返回值赋给了FutureTask
类的属性outcome
。联想到main()
方法中通过get()
方法获取线程返回值。然后查看FutureTask
类中的get()
方法,代码如下:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
可以看到最终返回的是report(s)
的方法,继续追踪到report()
方法,源码如下:
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
可以看到,方法中将outcome
属性的值赋给x
, 然后将x
返回出去。最总在main()
方法中获得返回的值。