此篇接上一篇Java面试之什么是多线程(一)
Java多线程是Java语言中的一个重要特性,它可以实现并发处理、提高程序的性能和响应能力。开发者需要了解多线程的概念和机制,并采用合适的多线程编程模型和同步机制,以保证程序的正确性和稳定性。Java提供了多种方式来实现多线程,下面列举了其中几种:
1.继承Thread类。通过继承Thread类并实现run()方法,创建一个线程类,然后通过创建该类的对象并调用start()方法来启动线程。
2.实现Runnable接口。通过实现Runnable接口并实现run()方法,创建一个线程任务类,然后通过创建Thread对象并传递该任务对象,并调用start()方法来启动线程。
Thread(T)和Runnable(R)
1.T是一个类,R是接口,因为在Java语言里面的继承特性,接口可以支持多继承,而类只能单一继承。所以如果在已经存在继承关系的类里面要实现线程的话,只能实现R接口。
2.R表示一个线程的顶级接口,T类其实是实现了R这个接口,我们在使用的时候都需要实现run方法。站在面向对象的思想来说,R相当于一个任务,而T才是真正处理的线程,所以我们只需要用R去定义一个具体的任务,然后交给T去处理就可以了,这样达到了松耦合的设计目的。
3.接口表示一种规范或者标准,而实现类表示对这个规范或者标准的实现。所以站在线程的角度来说,T才是真正意义上的线程实现。
4.R表示线程要执行的任务,因此在线程池里面,提交一个任务传递的类型是R。
总的来说,T只是实现了R接口并做了扩展,所以这两者站在个人角度认为没什么可比性。
3.实现Callable接口。通过实现Callable接口并实现call()方法,创建一个带返回值的线程任务类,然后通过创建FutureTask对象并传递该任务对象,再创建Thread对象并传递FutureTask对象,并调用start()方法来启动线程。
Callable、Future和CompletableFuture
Callable是Java多线程编程中的一个接口,用于实现带返回值的线程任务。与Runnable不同,Callable的call()方法可以返回一个结果,可以使用泛型来指定返回值的类型。在实际应用中,我们可以将Callable任务提交给线程池,或者使用FutureTask来执行并获取任务的返回值。
Future是Java并发编程中的一个接口,用于表示一个异步计算的结果。Future对象可以在计算未完成时获取计算的状态,并在计算完成后获取计算的结果。在Java中,通过Future来获取Callable任务的返回值。
需要注意的是,在使用Future获取结果时,如果任务尚未完成,会阻塞当前线程,直到任务完成为止。因此,在实际应用中,需要根据具体情况来设置超时时间,避免程序长时间阻塞。
CompletableFuture是Java 8中新增的一个基于事件驱动的异步回调类。它继承了Future接口,并提供了更加丰富的异步编程支持。简单来说就是当使用异步线程去执行一个任务的时候,我们希望在任务结束以后触发一个后续动作,而CompletableFuture就可以实现这个功能。相比于Future,CompletableFuture具有以下优点:
1.异步编程更加灵活。CompletableFuture可以通过回调函数(Callback)的方式实现异步编程,而不必像Future那样阻塞等待结果。
2.支持链式调用。通过链式调用的方式,可以更加优雅地组织异步计算任务。
3.提供异常处理机制。CompletableFuture提供了异常处理方法,可以更加方便地处理异步计算过程中可能发生的异常。
4.使用线程池。通过创建线程池来管理和复用线程,减少线程的频繁创建和销毁带来的新能开销,因为线程创建会涉及到CPU上下文切换、内存分配等工作。可以提高程序的性能和效率。(关于线程池的详细内容后面再补充)
5.使用J.U.C并发包中的工具类。Java并发包中提供了许多工具类,如Semaphore、CountDownLatch、CyclicBarrier、Lock、ReadWriteLock等,可以用来实现多线程的同步和协作。(关于AQS的详细内容后面再补充)
需要注意的是,多线程编程需要特别注意线程安全和同步问题,避免出现死锁、数据竞争等问题。关于多线程安全问题,请关注之后的Java多线程相关内容。
喜欢的朋友记得点赞、收藏、关注哦!!!