先记住一句话:线程操作资源类
一、Java使用多线程主要有三种方式:
第一种:继承Thread类
步骤:
① 定义一个类,继承Thread类,并重写Thead类的run方法,run方法内的内容为该线程要执行的任务。run方法也被称为执行体。
② 创建Thead子类的实例,即创建线程对象。
③ 使用线程的start方法启动线程。
第二种:实现Runnable方法,Java8以后可以结合lambda表达式使用(Runnable接口的实现类并不是线程类,只是线程运行的目标类,即线程要干的事儿,执行线程还需要依赖Thread类)
步骤:
① 定义一个类,实现Runnable接口,并重写该接口的run方法,run方法体仍然是执行体。
② 创建一个Thead类的实例,并将实现Runnable接口的类的实例作为参数传入Thead类的构造器中。
③ 调用Thread类实例的start方法启动线程。
第三种:实现Callable接口(需使用Future接口的实现类配合)
步骤:
① 定义一个类A,实现Callable接口,并重写该接口的call方法,call方法体仍然是执行体,call方法有返回值。
② 创建实现Callable接口的类A的实例B,并使用Future接口的实现类FutureTask子类包装B,FutureTask对象封装了B的call方法的返回值。
③ 创建一个Thead类的实例,并将FutureTask的类的实例作为参数传入Thead类的构造器中。
④ 调用Thread类实例的start方法启动线程。
⑤ 需要时,调用FutureTask类的实例的get方法获取call方法的返回值。
二、创建线程的三种方式的对比
采用实现Runnable、Callable接口的方式创建多线程时,优势是:
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类(java类可实现多个接口,但只能继承一个父类)。
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势是:
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
使用继承Thread类的方式创建多线程时优势是:
编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势是:
线程类已经继承了Thread类,所以不能再继承其他父类。
三、使用线程池执行操作
jdk1.5之后新增的java.util.concurrent包中提供了很多种线程池的实现:
image可以使用 Executors 进行创建,只需创建线程对象,放入调用ExecutorService的execute方法并将创建的线程对象传入即可(推荐使用线程池的submit方法,因为submit方法可以提交实现Callable接口的类),无需用户再手动启动线程,同时可以提高线程的利用率。上述都是旧的使用方式了,因为线程池中无界队列可能导致内存溢出的问题,不想被领导爆锤,就按照阿里规范来,其中明确要求线程池的使用要自己创建并指定队列长度。
另外提一下线程池的体系结构:
java.util.concurrent.Executor : 负责线程的执行和调度的根接口
||-- **ExecutorService 子接口 : 线程池的主要接口
||-- ThreadPoolExecutor : 线程池的实现类
||-- ScheduleExecutorService 子接口:负责线程的调度
||-- ScheduledThreadPoolExecutor : 继承ThreadPoolExecutor 实现 ScheduleExecutorService
网友评论