
 
目录
- 1、Oracle官网的文档是如何写的?
- 2、两种方法的对比
- 3、思考题:同时用两种方法会怎么样?
- 4、总结:最精准的描述
- 5、典型错误观点分析
- 6、典型错误观点总结
- 7、实现多线程——常见面试问题
1、Oracle官网的文档是如何写的?
- 方法一:实现Runnable接口
- 方法二:继承Thread类
实现Runnable接口
package threadcoreknowledge.createthreads;
/**
 * 描述: 用Runnable方式创建线程
 */
public class RunnableStyle implements Runnable{
    public static void main(String[] args) {
        Thread thread = new Thread(new RunnableStyle());
        thread.start();
    }
    public void run() {
        System.out.println("用Runnable方式创建线程");
    }
}继承Thread类
package threadcoreknowledge.createthreads;
/**
 * 描述:  用Thread方式实现线程
 */
public class ThreadStyle extends Thread{
    
    public void run() {
        System.out.println("用Thread方式实现线程");
    }
    public static void main(String[] args) {
        new ThreadStyle().start(); 
    }
}2、两种方法的对比
方法1(实现Runnable接口)更好。
方法2的缺点:
- 从代码的架构来考虑,具体执行的任务(run方法里面的内容)应该是和Thread解耦的,不应该把这两件事情混为一谈;
- 从资源的节约上,继承了 Thread 类,每次我们想新建一个任务只能去新建一个独立的线程,而新建一个独立的线程损耗是十分大的(因为需要去创建,销毁);
- 继承了Thread 类,由于Java不支持双继承,导致这个类无法去继承其他类了,这大大限制了代码的可扩性。
两种方法的本质对比
- 方法一:最终调用target.run();
    public void run() {
        if (target != null) {
            target.run();
        }
    }- 方法二:run()整个都被重写
3、思考题:同时用两种方法会怎么样?
package threadcoreknowledge.createthreads;
/**
 * 描述  同时使用Runnable和Thread两种实现线程的方式
 */
public class BothRunnableThread {
    public static void main(String[] args) {
        new Thread(new Runnable() {  //传入Runnable对象
            
            public void run() {
                System.out.println("我来自Runnable");
            }
        }){
                           //重写run()方法
            public void run() {         
                System.out.println("我来自Thread");
            }
        }.start();
    }
}代码执行结果:
从面向对象的思想去考虑: 因为我们重写了run()方法,所以导致Thread的三行run()方法代码不再存在,即使传入传入Runnable对象,它也不再执行。
4、总结:最精准的描述
1、通常我们可以分为两类,Orracle也是这么说的
2、准确的讲,创建线程只有—种方式那就是构造Thread类,而实现线程的执行单元有两种方式
- 方法一:实现Runnable接口的run方法,并把Runnable实例传给Thread类;
- 方法二:重写Thread的run方法(继承Thread类)。
5、典型错误观点分析
1、“线程池创建线程也算是一种新建线程的方式”
package threadcoreknowledge.wrongways;
import javafx.concurrent.Task;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPools {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000; i++) {
            executorService.submit(new Tasks() {
            });
        }
    }
}
class Tasks implements Runnable{
    
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
    }
}代码执行结果:
public Thread newThread(Runnable r) {
  Thread t = new Thread(group, r,
     namePrefix + threadNumber.getAndIncrement(),
              0);
  if (t.isDaemon())
    t.setDaemon(false);
  if (t.getPriority() != Thread.NORM_PRIORITY)
    t.setPriority(Thread.NORM_PRIORITY);
  return t;
}点进去源码可以看到,线程池本质创建线程的方法是new Thread,因此这并不是一种新的创建线程方式 。
2、“通过Callable和FutureTask创建线程,也算是一种新建线程的方式”
 本质是实现Runnable接口和继承Thread类实现的。
本质是实现Runnable接口和继承Thread类实现的。
3、“无返回值是实现runnable接口,有返回值是实现callable接口,所以callable是新的实现线程的方式”
本质依旧是实现Runnable接口和继承Thread类实现的。
4、定时器
package threadcoreknowledge.wrongways;
import java.util.Timer;
import java.util.TimerTask;
/**
 * 描述:      定时器创建线程
 */
public class DemoTimerTask {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        },1000,1000);
    }
}代码执行结果:
5、匿名内部类
package threadcoreknowledge.wrongways;
public class AnonymousInnerClassDemo {
    public static void main(String[] args) {
        new Thread(){
            
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }.start();
        new Thread(new Runnable() {
            
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }).start();
    }
}代码执行结果:

本质一样。
6、Lambda表达式
package threadcoreknowledge.wrongways;
public class Lambda {
    public static void main(String[] args) {
        new Thread(()-> System.out.println(Thread.currentThread().getName())).start();
    }
}代码执行结果:

本质一样。
6、典型错误观点总结
多线程的实现方式,在代码中写法千变万化,但其本质万变不离其宗。他们通过各种各样的包装,比如线程池,定时器,包装的外表好像是实现线程池的一种方式,但是我们打开包装透过源码去看到他最终实现的道理的话,本质其实是实现Runnable接口和继承Thread类。
7、实现多线程——常见面试问题
有多少种实现线程的方法?思路有5点:
- 从不同的角度看,会有不同的答案。
- 典型答案是两种
- 我们看原理,两种本质都是一样的
- 具体展开说其他方式
- 结论
实现Runnable接口和继承Thread类哪种方式更好?
- 从代码架构角度
- 新建线程的损耗
- Java不支持双继承
                












