Java如何创建线程
首先关于“创建线程的三种方式”这种说辞,笔者认为容易产生歧义并为初学者带来不必要的误解,不如说是自定义线程的三种方式更为合理(但后文依然使用创建线程的说法),因为创建线程这一具体的操作,最终都是在本地方法start0()中完成的。
无论采用继承Thread类、实现Runnable接口还是实现Callable接口创建线程,最终创建时都要调用Thread的start()方法,start()方法中真正创建线程的是本地方法start0()。
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) {
}
}
}
// 本地方法 CPP实现线程创建
private native void start0();
start0映射到了C++的JVM_ENTRY函数中
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
bool throw_illegal_thread_state = false;
{
MutexLocker mu(Threads_lock); // 加锁
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
// 计算线程大小
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
size_t sz = size > 0 ? (size_t) size : 0;
// 创建Java线程
native_thread = new JavaThread(&thread_entry, sz);
if (native_thread->osthread() != NULL) {
native_thread->prepare(jthread);
}
}
}
Thread创建线程
自定义类继承Thread方法,并且重写run方法,需要创建线程时调用Thread的start()方法即可。
如果直接调用run()方法,包括使用类实现Runnable直接调用run()方法,或使用类实现Callable直接调用call()方法,本质都是当前线程(示例场景中为main线程)调用方法,并不会真正的创建线程。
public class Test {
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.start();
}
static class ThreadTest extends Thread{
@Override
public void run(){
System.out.println(this.toString());
}
}
}
测试输出如下:
Thread[Thread-0,5,main]
Process finished with exit code 0
Runnable创建线程
自定义类实现Runnable接口,实现run方法,需要创建线程时,需要创建新的Thread对象并将Runnable子类实例作为参数传入,调用Thread的start方法创建。
public class Test {
public static void main(String[] args) {
RunnableTest runnableTest = new RunnableTest();
new Thread(runnableTest).start();
}
static class RunnableTest implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread());
}
}
}
测试输出如下:
Thread[Thread-0,5,main]
Process finished with exit code 0
Callable创建线程
查询文档可以发现Thread的构造方法不支持Callable子类作为参数传入,因此需要利用FutureTask作为媒介(FutureTask是Runnable的实现类),FutureTask对象可以用于获取Callable子类的执行返回值,还可以判断线程是否执行完毕。
自定义类实现Callable接口,并实现call()方法,与Runnable不同,call()方法支持异常抛出以及返回值,需要创建线程时,先将Callable子类实例作为参数创建FutureTask对象,再以FutureTask实例作为参数创建Thread对象,调用Thread的start()方法。
public class Test {
public static void main(String[] args) {
CallableTest callableTest = new CallableTest();
FutureTask futureTask = new FutureTask(callableTest);
new Thread(futureTask).start();
try {
// 利用FutureTask判断线程是否执行完毕并获取返回值
while(!futureTask.isDone())
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
static class CallableTest implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread());
return 10;
}
}
}
测试输出如下:
Thread[Thread-0,5,main]
10
Process finished with exit code 0
区别
- 直接继承Thread类创建编码简单,但无法再继承其他类,其余两种相反。
- 使用Thread创建线程时,可以直接使用this关键字获取当前线程,Runnable和Callable需要调用Thread.currentThread()方法。
- 只有使用Callale创建线程,可以获取到线程的返回值,且支持异常抛出。