创建线程的四种方式

1. 继承Thread类

最原生的方式就是继承Thread类,重写run方法!
来看一段demo

  1. public static void main(String[] args) {
  2. Thread01 thread = new Thread01();
  3. thread.start();
  4. }
  5. private static class Thread01 extends Thread {
  6. @Override
  7. public void run() {
  8. System.out.println("我是第一种(最原生)方式,我的线程id:[" + Thread.currentThread().getId() + "]");
  9. System.out.println(NEW_LINE);
  10. }
  11. }
  1. 我是第一种(最原生)方式,我的线程id:[22]
  2. ********** **********

2. 实现Runnable接口

在实现Runnable接口创建线程的写法上有多种方式

2.1. 实现接口并重写run方法

  1. public static void main(String[] args) {
  2. // 实现接口方式
  3. new Thread(new Runnable01())
  4. .start();
  5. }
  6. private static class Runnable01 implements Runnable {
  7. @Override
  8. public void run() {
  9. System.out.println("实现了runnable接口的方式");
  10. }
  11. }

2.2. 使用匿名内部类

  1. public static void main(String[] args) {
  2. // 匿名内部类方式
  3. Runnable runnable = new Runnable() {
  4. @Override
  5. public void run() {
  6. System.out.println("我是第二种方式...");
  7. System.out.println(NEW_LINE);
  8. }
  9. };
  10. Thread thread = new Thread(runnable, "t1");
  11. thread.start();
  12. }

2.3. 使用Lambda表达式

Runnable接口被注解 @FunctionalInterface标注,且有且只有一个方法,这种函数式接口可以使用lambda表达式简化调用
image.png

  1. public static void main(String[] args) {
  2. new Thread(() -> System.out.println("我是第二种的新版写法~,使用了lambda表达式简化匿名内部类~\n"), "t2")
  3. .start();
  4. }

使用java8带来的新特性lambda表达式,可以极大的简化代码以及提高代码的可读性~~

3. Callable+ FutureTask

  1. public static void main(String[] args) {
  2. // 使用lambda简化
  3. // lambda函数式接口方式
  4. Callable<Integer> callable = () -> {
  5. int i = 1000;
  6. while (i > 10) {
  7. i /= 2.5;
  8. }
  9. System.out.println("我是第三种方式,计算结果为:[" + i + "]");
  10. return i;
  11. };
  12. FutureTask<Integer> futureTask = new FutureTask<>(callable);
  13. new Thread(futureTask, "t3")
  14. .start();
  15. try {
  16. /*
  17. * 使用futureTask的最大好处就是可以使用阻塞等待获取结果 在此时即为同步
  18. */
  19. Integer result = futureTask.get();
  20. } catch (InterruptedException | ExecutionException e) {
  21. e.printStackTrace();
  22. }
  23. }

补充:

  • Callable接口有返回值,不同于Runnable。
  • FutureTask#get() 方法,会等待直到将Callable接口中的call方法执行完,此时相当于同步阻塞等待结果值。

4. 使用线程池

请参考:线程池

总结

  1. 使用继承Thread类实现Runnable接口两种方式都没有返回值。而使用了Callable接口有返回值。
  2. 使用前三种方式都不能控制资源,即可能出现无限申请线程,导致OOM(内存溢出)。
  3. 只有线程池可以控制资源,并且性能稳定!
  4. 工作中,无非必要不要使用线程,如果非要使用,请使用线程池,不要显示创建线程!!

完整实例 -> CreateThreadDemo