①创建一个继承于java.lang.Thread的类
public class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("线程名字:"+Thread.currentThread().getName() + "\t线程ID:"
+ Thread.currentThread().getId());
}
}
public class MyThreadTest {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
myThread1.start();
}
}
②实现java.lang.Runnable接口,重写run()方法,然后使用Thread类来包装
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("线程名字:"+Thread.currentThread().getName() + "\t线程ID:"
+ Thread.currentThread().getId());
}
}
public class ThreadTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
- 这两种方式实现多线程的方式都是围绕着Thread类展开的,第一种方式实现线程是继承于java.lang.Thread类,然后实现run方法,最终调用Thread类中的start方法启动线程。而实现Runnable接口,则是把run写到了接口中,并由实现类实现run方法,然后再用Thread类对Runnable接口进行包装,然后在调用Thread中start方法启动线程。①②两种方式都是最终都是调用Thread中start方法进行启动线程。
- 两种方式在本质上没有明显的区别,但是在外观上有很大的区别,第一种方式是继承Thread类,由于Java是单继承,如果一个类继承了Thread类,那么就没办法继承其它的类了,在继承上有一点受制,有一点不灵活,第二种方式就是为了解决第一种方式的单继承不灵活的问题,所以平常使用就建议使用第二种方式。
③ 实现 java.util.concurrent.Callable,用java.util.concurrent.FutureTask类对其包装,然后再用java.lang.Thread类对java.util.concurrent.FutureTask的实现类进行包装
public class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tstarting...");
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tover...");
return sum;
}
}
public class ThreadTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyCallable myCallable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
Thread thread = new Thread(futureTask);
thread.start();
Integer res = futureTask.get();
System.out.println("线程执行结果:"+res);
}
}
三种方式比较:
- Thread: 继承方式, 不建议使用, 因为Java是单继承的,继承了Thread就没办法继承其它类了,不够灵活
- Runnable: 实现接口,比Thread类更加灵活,没有单继承的限制
- Callable: Thread和Runnable都是重写的run()方法并且没有返回值,Callable是重写的call()方法并且有返回值并可以借助FutureTask类来判断线程是否已经执行完毕或者取消线程执行
结论
- 当线程不需要返回值时使用Runnable,需要返回值时就使用Callable,一般情况下不直接把线程体代码放到Thread类中,一般通过Thread类来启动线程
- Thread类是实现Runnable,
- Callable封装成FutureTask,FutureTask实现RunnableFuture,RunnableFuture继承Runnable,所以Callable也算是一种Runnable,
- 所以三种实现方式本质上都是对Runnable实现