一:继承Thread类
- 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体
- 创建Thread子类的实例,即创建了线程对象
- 调用线程对象的start()方法来启动该线程
public class ExtendsThreadTest extends Thread{
//重写run方法
@Override
public void run(){
System.out.println(Thread.currentThread().getName());
或
System.out.println(getName());
或
System.out.println(this.getName());
}
public static void main(String[] args){
//创建两个线程
new ExtendsThreadTest().start();
new ExtendsThreadTest().start();
}
}
Thread实现Runnable接口:class Thread implements Runnable
Thread实现的Runnable的run方法是下面这样的,可以看出run方法实现非常简单,所以我们如果想让线程实现我们要做的事情,可重写run方法:
@Override
public void run() {
if (target != null) {
target.run();
}
}
二:实现Runnable接口
- 定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体
- 创建 Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象
- 调用线程对象的start()方法来启动该线程
public class ImplementsRunnableTest implements Runnable {
//实现接口的run方法
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args){
ImplementsRunnableTest t = new ImplementsRunnableTest();
//创建两个线程
new Thread(t).start();
new Thread(t).start();
}
}
三:通过Callable和Future创建
- 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值
- 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(FutureTask是一个包装器,它通过接受Callable来创建,它实现了RunnableFuture接口,而RunnableFuture又同时实现了Future和Runnable接口,所以简介的实现了Runnable接口
- 使用FutureTask对象作为Thread对象的target创建并启动新线程
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
public class ThirdThreadTest implements Callable<String> {
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
public static void main(String[] args){
//创建callable实现类实例
ThirdThreadTest t = new ThirdThreadTest();
//使用FutureTask类来包装Callable对象
FutureTask<String> ft1= new FutureTask<String>(t);
FutureTask<String> ft2= new FutureTask<String>(t);
new Thread(ft1).start();
new Thread(ft2).start();
try {
System.out.println(ft1.get());
System.out.println(ft2.get());
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
四:通过所线程池
- 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值
- 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口)
- 使用FutureTask对象作为Thread对象的target创建并启动新线程
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
public class ThreadPoolTest implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args){
//创建线程池
ExecutorService e = Executors.newFixedThreadPool(5);
ThreadPoolTest t = new ThreadPoolTest();
for(int i=0;i<3;i++){
//为线程池分配任务
// Future f = e.submit(t);
// if (f.get() == null ){
// System.out.println("执行完成");
// }
e.execute(t);
}
//关闭线程池
e.shutdown();
}
}
创建线程可以使用submit()或execute(),两者区别为:
- submit()有返回值,execute()无返回值
void execute(Runnable command);
,利用Future .get()去的线程执行的结果,如果等于null,表示执行成功 - submit()方法有3种参数类型,execute只有1种参数
void execute(Runnable command);
1. <T> Future<T> submit(Callable<T> task);
2. <T> Future<T> submit(Runnable task, T result);
3. Future<?> submit(Runnable task);
四:四种创建方法的区别
- 使用创建Thread子类的方法,只能继承Thread类,所以不能再继承其它父类了,而实现了Runnable接口或Callable接口,还可继承其它的类,更加面向对象
- Runnable接口或Callable接口时,要访问当前线程必须使用Thread.currentThread()方法,而继承Thread直接使用this即可获得当前线程
- Callable任务执行后有返回值,而Runnable没有返回值
- call方法可抛出异常,而run方法不可以
总结
- 其实归根到底,所有的实现方法都是实现Runnable接口创建线程
- 只有在启动线程的start方法时,才有执行线程要完成的任务,即run方法