这里不会贴代码,只是将创建线程的三种方法做个笼统的介绍,再根据源码添加上自己的分析。
通过三种方法可以创建java线程:
1、继承Thread类。
2、实现Runnable接口。
3、实现Callable接口,并通过FutureTask的实例注入到Thread的target中。
创建java线程首要的目的是通过新创建的线程执行自己的代码逻辑,就是实现重载run方法。
所以我们无论是继承Thread类还是实现Runnable接口,要做的就是重载run方法,实现自己的代码逻辑。
接下来是运行:无论使用哪种方法实现自定义线程,我们都要创建线程实例(继承Thread类的方式是通过派生类对象的创建,从而创建父类对象)。然后通过执行Thread实例的Start方法,将该实例加入到ThreadGroup中,然后通过native方法start0运行该Thread实例。
经过一系列运行状态的检测(具体干了啥,都在native方法中),最终调用Thread实例的run方法(实现Runnable接口)或者调用Thread派生类实例的run方法(继承Thread类)。
实现Runnable和继承Thread的区别在于继承Thread类的线程类不能再继承其他父类(Java单继承决定)。当然有的博文表示实现Runnable接口的方式可以使Runnable实现类的代码被多个Thread共享,其实只是将Runnale的实现类的实例注入到了多个Thread的target字段中,这些Thread实例操作的是同一个Runnable接口的实现类实例。这当然可以实现共享,但Runnable的子类如Thread都可以被注入到Thread类的target中。
使用Runnable实现run方法在设计模式上更加符合面向对象组合的要求。
还有一种方式是使用Callable<T>接口,这种方式实质上和Runnable是一致的。使用Callable<T>能够使线程返回类型为T的值。
主要方法是:
FutureTask<Integer> futureTask = new FutureTask<Integer>((Callable<Integer>)()->{ System.out.println("callable create thread"); return 5; } )
new Thread().start();
然后通过使用FutureTask类的get方法获取返回值,该方法会阻塞直到线程退出。FutureTask继承了Future<T>和Runnable接口,所以能够被注入到target中。
线程执行后,调用FutureTask实例的run方法,该方法中调用callable的call方法,因此,Callable中的call方法可以存放自定义线程代码逻辑的实现。
call方法的返回值被存放在outcome属性中。然后通过FutureTask的get方法获取。