文章目录
新增两种创建线程的方式
我们知道在Java中最常见的实现多线程的方式就是继承Thread类和实现Runnable接口,我们总是会根据实际需求来选择相应的实现方式。相比较而言,通过实现Runnable接口来实现多线程更有优势,详细的内容可以看java中的线程(线程\程序\进程、创建线程、图解多线程原理、Thread类中的方法、线程优先级)
但是以上两种方式实现多线程它们的run方法都没有返回值,这有时候就不能满足我们编程中的所需。
一、实现Callable接口
概述
1、实现Callable接口和实现Runnable接口都可以实现多线程,但是实现Runnable接口的run()方法是没有返回值的,而Callable接口可以返回指定类型的值。
2、Callable接口在java.util.consurrent包中,JDK 1.5之后提出。
3、Callable接口本身并不具备执行能力,因此Java提供了一个FutureTask类来使用Callable接口定义的具有返回值的任务。
4、Callable的对象必须先封装到FutureTask中,然后通过FutureTask传给Thread类,这样才能启动多线程。
5、FutureTask的get()方法可以获取到Callable接口实现任务的返回值。
实现Callable接口中各个类之间的关系
首先提出一个问题:我们为什么要把Callable的对象先封装到FutureTask中,然后通过FutureTask传给Thread类?
答:① Callable接口中只有一个方法如下,因此Callable接口没有任何的执行能力。
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
② 而FutureTask有两个构造方法如下,它可以传入一个Callable类型的对象。
构造方法 | 功能 |
---|---|
FutureTask(Callable callable) | 创建一个 FutureTask ,运行时执行给定的 Callable |
FutureTask(Runnable runnable, V result) | 运行时执行给定的 Runnable ,并使用get返回结果 |
③ FutureTask实现了RunnableFuture< V >接口,而RunnableFuture< V >接口继承了Runnable, Future< V > ,因此FutureTask的对象可以传给Thread类,并由Thread类调用start()方法执行线程。
它们之间的关系图如下:
Callable接口的使用
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/*实现Callable<>接口*/
public class MyCallable<String> implements Callable<String> {
/*重写call()方法,有返回值*/
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 0; i <= 50; i++) {
sum = sum+i;
System.out.println(Thread.currentThread().getName()+":"+sum);
}
return (String) (Thread.currentThread().getName()+"计算的50以内的整数和为"+sum);
}
}
class TestMyCallable{
/*这是一个程序入口*/
public static void main(String[] args) {
/*创建实现Callable接口的类的实例化对象*/
MyCallable<String> myCallable = new MyCallable<>();
/*创建FutureTask类的实例化对象,并传入Callable接口的实例化对象*/
FutureTask<String> futureTask = new FutureTask<>(myCallable);
/*创建Thread类的实例化对象,传入FutureTask的实例化对象*/
Thread thread = new Thread(futureTask,"线程1号");
/*启动线程*/
thread.start();
/*通过FutureTask的get方法获取返回值*/
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
二、使用线程池创建线程
什么情况下需要使用线程池?
当我们经常创建和销毁、使用量特别大的资源时,比如并发情况下的线程。对性能的影响很大。
线程池的创建思路是什么?
提前创建好n个线程,放入到线程池中,使用时获取,使用完之后再放回池子。这样的好处就是避免了频繁创建销毁、实现了重复利用。
线程池描述
线程池本身就是一个容器,这个容器是以集合LinkedList< Thread>为底层实现的,当程序第一次启动的时候,创建了多个线程,保存到一个集合之中,当我们想要使用的时候,就从集合中取出线程使用。
线程池的使用
Executors
1、java.util.concurrent.Executors:线程池的工厂类,用来创建线程池,返回不同类型的线程池。
2、静态方法
(1)static ExecutorService newFixedThreadPool(int nThreads) 创建一个固定线程数的线程池
参数:int nThreads:创建线程池中包含的线程数量
返回值:ExecutorService接口,返回的是一个ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收,面向接口编程
(2)Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池。
(3)Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池。
(4)Executors.newScheduledThreadPool(int corePoolSize):创建一个线程池,可以安排再给定延迟后运行命令或者定期地执行。
ExecutorService
1、java.util.concurrent.ExecutorService:线程池接口。
2、用来从线程池中获取线程,调用start方法,执行线程任务,submit(线程任务)提交一个线程任务用于执行。
3、关闭线程池地方法:void shutdown()。
线程池的代码实现
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyPool {
/*这是一个程序入口*/
public static void main(String[] args) {
/*①使用线程池的工厂类Executors提供的静态方法newFixedThreadPool生产一个指定数量的线程池*/
ExecutorService pool = Executors.newFixedThreadPool(3);
/*③创建线程任务对象*/
Thread1 t1 = new Thread1();
/*③创建线程任务对象*/
Thread2 t2 = new Thread2();
/*④调用ExecutorService中的submit()方法,传递线程任务,开启线程*/
pool.submit(t1);
pool.submit(t2);
pool.submit(t1);
pool.submit(t2);
pool.submit(t1);
pool.submit(t2);
pool.submit(t1);
pool.submit(t2);
/*⑤调用ExecutorService中的shutdown()方法销毁线程池*/
pool.shutdown();
}
}
/*②创建线程任务类*/
class Thread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+"完成任务!!!");
}
}
}
/*②创建线程任务类*/
class Thread2 implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":完成任务!!!");
}
return null;
}
}