JAVA中创建线程的三种方式的使用、区别与优缺点

JAVA中通过继承Thread类、实现Runnable接口以及实现Callable接口配合Future接口实现创建多线程,三种方式各有优缺点,而第三种则具备更多的增强能力

JAVA中创建线程的三种方式的使用、区别与优缺点

所属分类: JAVA
专题标签: 方法 创建 多线程

创建线程

继承Thread类

  1. /**
  2. * 通过继承Thread类实现多线程
  3. */
  4. public class MyThread extends Thread {
  5. public MyThread() {
  6. System.out.println("构造!调用线程名:"+Thread.currentThread().getName());
  7. }
  8. @Override
  9. public void run() {
  10. System.out.println("线程启动了!本线程名:"+getName());
  11. for(int i=0;i<100;i++){
  12. System.out.println(getName()+"++"+ i);
  13. }
  14. }
  15. // 测试代码
  16. public static void main(String[] args)
  17. {
  18. // 直接创建自定的继承类
  19. new MyThread().start();
  20. new MyThread().start();
  21. }
  22. }

实现Runnable接口

  1. /**
  2. * 通过实现Runnable接口创建线程类
  3. */
  4. public class MyRunnable implements Runnable {
  5. @Override
  6. public void run() {
  7. System.out.println("线程启动了!");
  8. }
  9. // 测试代码
  10. public static void main(String[] args)
  11. {
  12. // 直接运行
  13. new Thread(new MyRunnable()).start();
  14. // 想获取当前线程的信息
  15. Thread thread = new Thread(new MyRunnable());
  16. thread.start();
  17. System.out.println("线程启动了!线程的名字;"+thread.getName());
  18. // 匿名内部类写法
  19. new Thread(new Runnable(){
  20. @Override
  21. public void run() {
  22. System.out.println("匿名内部类线程启动了!");
  23. }
  24. }).start();
  25. // JDK1.8 lambda写法
  26. new Thread(() -> {
  27. System.out.println("lambda写法匿名内部类线程启动了");
  28. }).start();
  29. }
  30. }

实现Callable接口配合Future

  1. /**
  2. * 实现Callable接口配合Future创建线程
  3. */
  4. public class MyCallable implements Callable<String> {
  5. @Override
  6. public String call() throws Exception {
  7. Thread.sleep(5000);
  8. System.out.println("子线程名:"+Thread.currentThread().getName());
  9. return "返回值1";
  10. }
  11. // 测试代码
  12. public static void main(String[] args)
  13. {
  14. MyCallable myCallable = new MyCallable();
  15. // FutureTask能用来包装一个Callable或Runnable对象,因为它实现了Runnable接口,而且它能被传递到Executor进行执行
  16. // RunnableFuture接口同时继承Future接口和Runnable接口,在执行run()方法后,可以通过Future访问执行结果,实现类是FutureTask
  17. FutureTask<String> task = new FutureTask<String>(myCallable);
  18. new Thread(task).start();
  19. try {
  20. //取消任务
  21. //task.cancel(true);
  22. //完成状态
  23. //task.isDone();
  24. //取消状态
  25. //System.out.println("子线程的取消了吗:"+task.isCancelled());
  26. System.out.println("子线程的返回值:"+task.get());
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. } catch (ExecutionException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }

优缺点

继承Thread类

优点:

  • 编写简单
  • run()方法内访问当前线程,使用this即可

缺点:

  • 线程类不能再继承其他父类

实现Runnable或Callable接口

优点:

  • 线程类只是实现了接口,还可以继承其他类
  • 多个线程可以共享同一个target对象,适合多个相同线程来处理同一份资源

缺点:

  • (不算缺点的缺点)访问当前线程,则必须使用Thread.currentThread()方法

Runnable或Callable区别

Callable方案(JDK1.5)可以理解为是Runnable的增强版,其关系如下:

区别如下:

  1. Callable的重写方法是call(),Runnable的重写方法是run()
  2. Callable的任务执行后可返回值,而Runnable的任务是不能返回值的
  3. Callable的call方法可以抛出异常,Runnable的run()方法不可以
  4. Callable任务可以拿到一个Future对象,表示异步计算的结果。
    提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
    可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

Future接口提供的方法

boolean cancel(boolean mayInterruptIfRunning)

取消Future里关联的Callable任务
mayInterruptIfRunning入参:是否允许在线程运行时中断

V get()

返回Callable任务里call()方法的返回值
调用该方法将导致程序阻塞,必须等到子线程结束以后才会得到返回值

V get(long timeout, TimeUnit unit)

返回Callable任务里call()方法的返回值
该方法让程序最多阻塞timeout和unit指定的时间
如果经过指定时间后,Callable任务依然没有返回值,将会抛出TimeoutException异常

boolean isCancelled()

如果Callable任务正常完成前被取消,则返回true

boolean isDone()

如果Callable任务已经完成, 则返回true