关于线程的主具体含义,这里就不多表述,下面主要是介绍JAVA多线程的实现方法。目前Java提供了三种线程的实现方式:Thread类/Runnable接口/FutureTask类。
这三种方法本质上都一样,都需要创建Thread类对象,调用Thread类的start方法启动,下面就介绍一下这三种方法的具体使用:
Thread类
直接继承Thread类,然后重新Thread类里的run()方法。如下图,定义ThreadA类,继承了Thread类,在main方法里,定义ThreadA的一个对象并且调用它的start方法。
Thread.start()方法的实现首先判断线程状态是不是NEW状态,否则抛出异常,然后把自己加入到线程组,然后调用本地方法start0()启动线程。
Runnable接口
定义一个类,实现Runnable接口,实现run方法,图中所示ThreadB类,在main方法中,除了创建ThreadB的对象,还需要创建一个Thread对象,因为在Java中只能通过Thread的start方法来启动线程,因此需要用ThreadB对象去初始化一个Thread对象,然后在用start方法启动线程。
FutureTask类
FutureTask实现了RunnableFuture接口,RunnableFuture接口继承了Runnable接口和Future接口。Runnable接口上面已经说到;Future接口代表异步计算的结果,通过Future接口提供的方法可以查看异步计算是否执行完成,或者等待执行结果并获取执行结果,同时还可以取消执行。具体Future的用法后面介绍。一般使用FutureTask是和线程池一起使用,线程池暂时不讨论,下面还是介绍通过Thread的方法来启动FutureTask。FutrueTask的使用需要创建一个实现了Runnable或者Callable的对象。Callable接口需要可以获取线程执行的结果,这里还需要定义一个接受线程执行执行结果的测试类:
实现Runable的类:
实现Callable的类:
main方法启动类:
上图所示,当用Runnable的实现类去初始化FutureTask的时候,需要传递一个用于接受线程执行结果的对象,因为Runnable接口中run方法没有返回值。
Java线程的启动在Thread类执行start方法的的时候,JVM虚拟机启动一个新的线程,该线程会去执行Thread类中的run方法,当线程是通过继承Thread的方式创建的,那么新线程执行的run方法就重写后的run方法内容。如果是Runnable的方式,如下图所示Thread.run方法源码:
新线程会执行这段代码,这里的target所引用的对象就是实现了Runnable接口的类的对象。因此调用target.run()就是调用Runnable对象的run方法。
FutureTask类的方式,如果是用Runnable对象创建的FutureTask实例,那么在FutureTask类的构造方法中,会把Runnable实例转换为Callable实例:
调用Executors.callable(runnable, result)可以把runnable转换为Callable,具体实现也比较简单:
在Executors.callable方法中用需要转换为Callable对象的Runnable实例创建一个RunnableAdapter实例,RunnableAdapter类实现了Callable接口:
在重写的call方法里调用所要转换的Runnable对象的run()方法,完成了从Runnable接口向Callable对象的转换。
如果是是通过Callable实例创建的FutureTask对象,则直接将Callable实例赋值给FutureTask类中的callable变量:
当用FutureTask对象来创建Thread变量,会把FutureTask对象赋值给Thread类中的target,那么虚拟机在新线程中执行的run方法,是FutureTask的run方法,具体FutureTask的run方法实现如下:
根据源码在FutureTask的run方法调用的就是初始化FutureTask对象是传给FutureTask类的Callable对象(包括把Runnabel对象所转化的)。
以上就是介绍了java线程的创建方法,下一节会介绍Java线程的基本属性和状态转换。
网友评论