参考:
Java
共有 3
种方式用来创建线程,分别是
- 继承
Thread
类或其子类 - 实现接口
Runnable
- 实现接口
Callable
和Future
主要内容:
- 线程构造器,
run()
和start()
方法 - 继承
Thread
类 - 实现接口
Runnable
- 实现接口
Callable
和Future
- 线程创建方式的比较
线程构造器,
run()
和
start()
方法
run()
和
start()
方法
Java
使用 java.lang.Thread
类及其子类作为线程类,创建新线程,必须是 Thread
类的子类
线程构造器
java.lang.Thread
的构造函数如下:
Thread() - Allocates a new Thread object.
Thread(Runnable target) - Allocates a new Thread object.
Thread(Runnable target, String name) - Allocates a new Thread object.
Thread(String name) - Allocates a new Thread object.
Thread(ThreadGroup group, Runnable target) - Allocates a new Thread object.
Thread(ThreadGroup group, Runnable target, String name) - Allocates a new Thread object so that it has target as its run object, has the specified name as its name, and belongs to the thread group referred to by group.
Thread(ThreadGroup group, Runnable target, String name, long stackSize) - Allocates a new Thread object so that it has target as its run object, has the specified name as its name, and belongs to the thread group referred to by group, and has the specified stack size.
Thread(ThreadGroup group, String name) - Allocates a new Thread object.
共有 8
个构造函数,本次学习暂不涉及 线程组 的概念,所以在使用过程中仅用到前 4
种构造方法
run()
参考: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();
}
}
创建线程的目的就是为了独立执行一段程序。启动线程后,Java
将自动调用 run()
方法,run()
方法执行完成或意外终止,表示该线程已结束,所以将执行程序封装在 run()
方法中
start()
参考: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 */
}
}
}
创建线程类实例后,调用 start()
方法,Java
虚拟机就会调用该线程的 run()
方法(不一定是实时的,取决于 Java
虚拟机的线程调度器的调度)
Note 1:必须调用线程的 start()
方法才能启动线程,所以直接调用 run()
方法只是顺序执行方法里的程序而已
Note 2:单个线程仅能调用一次 start()
方法,即单个线程实例仅能运行一次;如果调用多次 start()
方法,将会抛出 IllegalThreadStateException
异常
继承
Thread
类
Thread
类
继承 Thread
类及其子类,并实现 run()
方法。示例如下:
/**
* Created by zj on 2017/10/13.
*/
public class MyThread extends Thread {
@Override
public void run() {
super.run();
}
}
创建类对象,可使用构造函数如下:
Thread() - Allocates a new Thread object.
Thread(String name) - Allocates a new Thread object.
调用 start()
方法即可启动线程
实现接口
Runnable
Runnable
继承接口 Runnable
,并实现接口方法 run()
。示例如下:
/**
* Created by zj on 2017/10/16.
*/
public class MyRunnable implements Runnable {
@Override
public void run() {
}
}
创建 MyRunnable
对象,同时需要新建一个 Thread
类对象,将 MyRunnable
对象作为参数输入到 Thread
类对象中,可使用构造函数如下:
Thread(Runnable target) - Allocates a new Thread object.
Thread(Runnable target, String name) - Allocates a new Thread object.
示例如下:
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
得到新线程后,调用 start()
方法即可启动线程
实现接口
Callable
和
Future
Callable
和
Future
从 JDK 1.5
开始,Java
提供了新的接口 Callable
:
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
该接口仅有一个 call()
方法,其功能和 run()
类似,同样是执行线程的程序,不过它增加了以下功能:
- 可以有返回值
- 可以抛出异常
由于 call()
方法是 Java
在线程启动后自动调用的,无法正常返回,所以 Java
提供了一个接口 Future
,用于得到 call()
方法的返回值;同时,Java
内置了一个 Future
接口的实现类 FutureTask
,该接口需要 Callable
接口作为参数,同时也实现了 Runnable
接口,所以可以作为 Thread
类构造函数的参数
使用流程如下:
创建一个
Callable
接口实现类class MyCallable implements Callable<String> { @Override public String call() throws Exception { System.out.println("Hello Callable"); return "Hello World"; } }
创建
Callable
实例,作为参数创建FutureTask
实例MyCallable callable = new MyCallable(); FutureTask<String> futureTask = new FutureTask<String>(callable);
创建线程,使用
FutureTask
实例作为参数,启动start()
方法new Thread(futureTask).start();
调用
FutureTask
的get()
方法,即可获得返回值try { String str = futureTask.get(); System.out.println(str); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }
完整代码如下:
import java.util.concurrent.*;
/**
* Created by zj on 2017/10/13.
*/
public class Main {
public static void main(String[] args) {
MyCallable callable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<String>(callable);
new Thread(futureTask).start();
try {
String str = futureTask.get();
System.out.println(str);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("Hello Callable");
return "Hello World";
}
}
Note:调用 get()
方法后,当前线程会被阻塞,直到得到返回值为止
线程创建方式的比较
第1 / 2种方法的比较
- 当使用继承
Thread
类的方法创建新线程时,由于单个线程仅能执行一次,所以每个线程的资源都是独立的; - 而当使用实现
Runnable
接口的方法时,可以创建单个Runnable
类对象,多个线程同时使用该对象作为参数,此时,这些线程共享该对象的资源
测试函数如下:
import java.util.concurrent.*;
/**
* Created by zj on 2017/10/13.
*/
public class Main {
public static void main(String[] args) {
MyRun myRun = new MyRun();
new Thread(myRun, "MyRun-1").start();
new Thread(myRun, "MyRun-2").start();
new Thread(myRun, "MyRun-3").start();
new MyThr("MyThr-1").start();
new MyThr("MyThr-2").start();
new MyThr("MyThr-3").start();
}
}
class MyThr extends Thread {
private int i;
public MyThr(String name) {
super(name);
}
@Override
public void run() {
for (; i < 100; i++) {
System.out.println("current Thread name = " + this.getName() + " i = " + i);
}
}
}
class MyRun implements Runnable {
private int i;
@Override
public void run() {
for (; i < 100; i++) {
System.out.println("current Thread name = " + Thread.currentThread().getName() + " i = " + i);
}
}
}
从资源共享的角度来看,推荐使用 Runnable
接口方式,缺点就是操作会比较复杂(涉及线程同步,通信问题)
Runnable
和 Callable
接口比较
Callable
接口是 Runnable
接口的增强版,如果需要返回值,可以选择使用 Callable
接口