java创建线程的方法
Posted 一字千金
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java创建线程的方法相关的知识,希望对你有一定的参考价值。
1.1 创建线程
1.1.1 无返回值的线程创建
package com.first;
public class ThreadTest
{
public static void main(String[] args) {
System.out.println("主线程ID:"+Thread.currentThread().getId());
MyThread thread1 = new MyThread("thread1");//(1)继承thread类,启动线程
thread1.start();
MyThread thread2 = new MyThread("thread2");//(2)继承thread类,直接调用方法
thread2.run();
MyRunnable runnable = new MyRunnable();//(3)实现Runnable方法,作为入参
Thread thread3= new Thread(runnable);
thread3.start();
Thread thread4 = new MyThread(runnable);//(4)继承类也实现接口,实际上用的还是类中的run方法;thread run
thread4.start();
}
}
class MyThread extends Thread//(1)继承thread类,重写run方法
{
private String name;
public MyThread(String name){
this.name = name;
}
public MyThread(Runnable runnable){//(4)Runnable入参传入
super(runnable);
}
@Override
public void run()
{
System.out.println(" thread run name:"+name+" 子线程ID:"+Thread.currentThread().getId());
}
}
class MyRunnable implements Runnable{//(3)实现runable方法
public MyRunnable()
{
}
@Override
public void run() {
System.out.println("MyRunnable 子线程ID:"+Thread.currentThread().getId());
}
}
运行结果
主线程ID:1
thread run name:thread2 子线程ID:1
thread run name:thread1 子线程ID:11
MyRunnable 子线程ID:13
thread run name:null 子线程ID:14
线程Thread实际上是实现了Runable接口,class Thread implements Runnable{},并且定义了一个引用private Runnable target;用于保存构造函数传入的继承类引用MyRunnable的入参,public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);} ,Thread重写了Runable接口的run函数
public void run() {
if (target != null) {
target.run();
}
}
所以如果是情况(1)子类继承Thread类,重写run()方法,则target为null,Thread类的run方法实现也没有用,因为被子类重写了。调用的是子类实现的方法;如果是情况(2),没有启动线程而是直接调用子类的run方法,则跟调用一般方法一样,只不过是主线程在调用方法,thread2被创建,但是没有被启动;如果像情况(3)因为传入了MyRunable对象的引用,所以target不在为空,而且没有子类重写Thread的run()方法,所以会直接调用Thread默认的run()方法,target不为空,所以执行的是传入的MyRunable的方法;如果是情况(4)即传入了MyRunable的方法,子类也继承实现了run()方法,则按照顺序,MyRunable作为入参传入,MyThread子类调用了supuer的构造函数,传给父类的target。因为MyThread子类,重写父类的run方法,所以,无论是否传入target参数,Thread类的run()方法实现都会被MyThread子类的run()方法重写。所以最后执行的是MyThread子类中实现的run方法。
总结下创建线程的方法主要有两种:(1)继承Thread类,重写该类的run()方法.(2)实现Runnable接口,实现接口的run方法,作为Thread构造函数的入参传入任务。Thread的start方法来创建一个新线程来执行该子任务。如果调用Runnable的run方法的话,是不会创建新线程的,这跟普通的方法调用没有任何区别。
这2种方式都可以用来创建线程去执行子任务,具体选择哪一种方式要看自己的需求。直接继承Thread类的话,可能比实现Runnable接口看起来更加简洁,但是由于Java只允许单继承,所以如果自定义类需要继承其他类,则只能选择实现Runnable接口。
1.1.2 有返回值的线程创建
上述两种方法,在执行完任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。所以需要有返回值的线程实现。基于FutureTask类,Callable接口,Runnable, Future接口实现带返回值的线程。Runnable 接口是提供run方法,FutureTask实现接口的run方法,作为线程的执行方法。Future接口则是提供了几个函数,用来操作线程的执行,获取线程返回值。FutureTask实现了RunnableFuture接口,RunnableFuture继承了Runnable, Future接口。Callable接口只有一个带返回值的call函数,需要继承实现Callable的call函数,call函数是线程真正要执行的内容,在FutureTask中实现的run方法中会调用call函数来执行线程的任务内容。
(1)FutureTask实现了RunnableFuture接口
public class FutureTask<V> implements RunnableFuture<V>
(2)RunnableFuture继承了Runnable, Future接口
public interface RunnableFuture<V> extends Runnable, Future<V>
{
void run();
}
(3)FutureTask构造函数
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;// FutureTask定义callable引用指向Callable对象
this.state = NEW; // ensure visibility of callable
}
(4)Callable的定义
public interface Callable<V> {
V call() throws Exception;//只有一个call函数,线程执行任务
}
(5)FutureTask中run函数的实现
public void run() {//实现Runable接口的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 {
result = c.call();//调用了callable的call函数
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);//将执行结果赋值给outcome引用
}
} 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)FutureTask中的set函数
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;//执行结果赋值给outcome
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
(7)FutureTask中get()函数
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);//等待线程执行完毕
return report(s);
}
(8)FutureTask中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);
}
(9)Future接口的方法定义
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;//线程操作方法
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
综上所述,FutureTask实现Runable接口,是为了实现线程执行函数run方法。FutureTask实现future接口,是为了实现Future操作线程的方法,例如V get()返回执行结果。FutureTask以Callable为入参,是要执行Callable中的线程任务call()方法。FutureTask的run()方法中会调用Callable的call()方法。
所以给出实际的例子
package com.first;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadTest
{
public static void main(String[] args) {
// 使用FutureTask来包装Callable对象
ThreadByCallable rt = new ThreadByCallable();//创建任务对象
FutureTask<Integer> task = new FutureTask<Integer>(rt);//(5)使用
// task继承了Runable接口,所以可以作为Thread入参
new Thread(task, "有返回值的线程").start();
try {
// 获取线程返回值
System.out.println("thread5:返回值:" + task.get());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
class ThreadByCallable implements Callable<Integer> {//创建类实现Callable接口
@Override
public Integer call() {
System.out.println("thread5:call" + Thread.currentThread().getName());
// call()方法有返回值
return 5;
}
}
以上是关于java创建线程的方法的主要内容,如果未能解决你的问题,请参考以下文章