为什么说本质上实现线程只有一种方式?
实现Runnable 接口
public class RunnableThread implements Runnable {
@Override
public void run() {
System.out.println("implents Runnable ...");
}
public static void main(String[] args) {
new Thread(new RunnableThread()).start();
}
}
第一种方式是通过实现Runnable接口实现多线程,如图所示,通过实现Runnable接口重写run方法,之后通过该对象创建Thread类实现多线程。
继承Thread类
public class ExtendsThread extends Thread {
@Override
public void run() {
System.out.println("extends Thread ...");
}
public static void main(String[] args) {
new ExtendsThread().start();
}
}
第二种方式是通过继承Thread类,后重写run方法实现多线程。
线程池创建线程
public class ThreadPoolThread {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 50, TimeUnit.SECONDS,
new PriorityBlockingQueue<>(), Executors.defaultThreadFactory());
executor.submit(() -> {
System.out.println("threadPool ...");
});
}
}
可以看到,如果我们创建线程池时不指定线程工厂时,默认使用DeafultThreadFactory工厂类来为线程池创建线程。那我们再看下DeafultThreadFactory类是如何创建线程的。
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
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;
}
}
从上面这段代码可以看出,DeafultThreadFactory创建线程是通过new Thread()方式创建的,并且会给线程设置名称、是否守护线程、线程优先级等等,因此本质上还是通过new Thread()方式创建的。
通过有返回值的Callable创建线程
public class CallableTask implements Callable<String> {
@Override
public String call() throws Exception {
return "Implements Callable ...";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Future<String> future = Executors.newScheduledThreadPool(10).submit(new CallableTask());
System.out.println(future.get());
}
}
第四种线程创建方式是通过有返回值的Callable创建线程,Runnable创建线程是无返回值的,而Callable和与之相关的Future、FutureTask,它们可以把线程的执行结果作为返回值返回。
但是无论是Callable还是FutureTask,它们首先需要被执行的,而不是说它们本身就是线程,submit()方法把任务放到线程池中,并由线程池创建线程。因此本质上还是通过继承Thread或实现Runnable的方式实现线程的。
其他创建方式
定时器Timer
实现线程的方式只有一种
从上面的案例中我们可以看到,无论是通过线程池、或是定时器Timer,亦或是Callable,本质上都是对new Thread()做了一层包装。但是无论是继承Thread或是实现Runnable接口,这两种方式本质上是一种。
public class Thread implements Runnable {
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
启动线程需要调用start()方法,而start()方法最终还是调用run()方法,而从上面代码块可以看出,当我们通过Runnable方式创建Thread,最后执行的还是target.run()方法,而target对象就是当前传进来的Runnable实现类。而继承Thread类会直接重写run方法。因此我们可以看出,实现一个线程的方式就是构建一个Thread类 new Thread()。(可以这样理解,不论是继承Thread类,还是实现Runnable接口,最终的目的都是为了new Thread())。