线程创建方式
- 继承Thread
- 实现Runable
- 实现Callable
Thread
Thread是普通类,实现多线程的方式是继承该类,并重写run()
方法,通过start()
方法启动线程。
Runable
Runable是接口,实现多线程的方式是实现该接口,重写run()
方法,并将该实现类对象传入Thread对象,通过Thread对象的start()
方法启动线程。
run()
方法体就是线程的执行体。
Thread和Runnable的关系
- Thread实现了Runable接口,在Runable的基础上提供了更加丰富的属性和方法(如
name
、state
、priority
、yield()
、sleep()
、join()
等)。 - 继承Thread的方式需要重写其
run()
方法,即子类对象中的run()
方法为线程执行体,调用start()
,底层会自动调用run()
方法执行线程体; - 实现了Runable接口的方式是重写其
run()
方法,并传入Thread对象,这种方法依旧是调用的Thread类对象的run()
方法。那么又是如何执行Runable实现类的run()
方法呢?可以看下下面Thead类的相关源码:
/* What will be run. */
private Runnable target;
/**
* 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) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
可以看到首先判断Runable实现类对象是否存在,然后执行Runable实现类对象中的run()
方法.
通过Thread继承实现的多线程,每new一个线程类都会有自己的对象空间。因此不同的线程类操作的对象属性是独立的。
而通过Runable接口实现的类,可以将一个Runable实现对象传入多个Thread对象,这样每个线程操作的Runable实现类的对象属性是同一个,但此时就会出现并发性问题。
看似Runable更容易实现多线程之间的资源共享,而Thread不可以,是错误的,只是创建多线程个数不同的问题。如果使用不同的Thread对象运行不同的Runable实现也会出现内存独立的现象。如果要实现Thread的资源共享,将Thread属性设置成static即可。所以Thread和Runable没有根本的区别,只是实现方式不同罢了。
Callable
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方法就是执行的线程体,可见,这种方式与以上两种线程方式不同:
call()
方法可以声明抛出异常call()
方法可以有返回值
下面是Callable实现多线程的方式:
public class FutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<String>(myCallable);
new Thread(futureTask).start();
System.out.println("主线程");
}
}
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("执行call()");
Thread.sleep(5000);
return "执行call()结束";
}
}
实现步骤:
1.实现Callable接口,在call()
方法中编写线程执行体;
2.用Callable实现类似对象例化FutureTask类,并指定线程执行结束返回值类型;
3.用FutureTask对象实例化Thread;
4.启动线程;
FutureTask
FutureTask是一个可取消的异步计算。可以调用方法去开始和取消一个计算,可以查询计算是否完成并且获取计算结果。
FutureTask实现了Runnable和Future接口,所以FutureTask可以像Runable一样创建一个线程。
我们可以通过下面的调用实现获取Callable中的call方法体的运算结果:
futureTask.get()
我们可以分析一下源码,首先,执行线程依旧是调用重写于Runable中的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);
}
}
判断当前线程处于新建状态,执行callable对象类对象的call()
方法,即执行线程体。最后执行set(result)
。
现在我们来看一下FuturTask的set()方法。
protected void set(V v) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v;
STATE.setRelease(this, NORMAL); // final state
finishCompletion();
}
}
在set()
方法中,线程体运行结果赋值给了属性outcome
。
下面是get()
方法和返回的report()
方法结果:
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
/**
* Returns result or throws exception for completed task.
*
* @param s completed state value
*/
@SuppressWarnings("unchecked")
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);
}
最后,在线程运行状态正常的情况下就返回了Callable的call方法的运算结果。
需要注意的是,futureTask.get()
的调用是阻塞式的,也就是说在线程未正常结束,主线程会一直等待其执行完毕,从而获得返回结果。
我们可以从get方法中得出此结论,s <= COMPLETING
,这里首先会进行一个线程状态的判断,如果线程还未执行结束,则将执行awaitDone()
方法
/**
* Awaits completion or aborts on interrupt or timeout.
*
* @param timed true if use timed waits
* @param nanos time to wait, if timed
* @return state upon completion or at timeout
*/
private int awaitDone(boolean timed, long nanos)
}
该方法注释已说明:等待完成或在中断或超时时中止。