1、继承Thread类的方式进行实现
java.lang.Thread是Java提供的线程类,继承了该类的就是一个线程类,所有的线程对象都必须是 Thread 类或其⼦类的实例。每个线程的作⽤是完成⼀定的任务,实际上就是执⾏⼀段程序流即⼀段顺序执⾏的代码,Java 使⽤线程执⾏体来代表这段程序流。主线程和分支线程是并发执行的,谁在前谁在后是随机的抢占式分配。Java中通过继承Thread类来创建并启动多线程的步骤如下:
1. 定义 Thread 类的⼦类,并重写该类的 run() ⽅法,该 run() ⽅法的⽅法体就代表了线程需要完成的任务,因此把 run() ⽅法称为线程执⾏体。
public class MyThread extends Thread{
@Override
public void run() {
// 线程要执行代码,getName()方法可以在线程启动时获取线程的名字
for (int i = 0; i < 15; i++) {
System.out.println(getName() + " Holle world");
}
}
}
2. 创建 Thread ⼦类的实例,即创建了线程对象
3. 调⽤线程对象的 start() ⽅法来启动该线程
public class ThreadDemo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
2、实现Runnable接口的方式进行实现
采⽤ java.lang.Runnable 实现也是⾮常常⻅的⼀种,只需要重写 run ⽅法即可。
步骤如下:
1. 定义 Runnable 接⼝的实现类,并重写该接⼝的 run() ⽅法,该 run() ⽅法的⽅法体同样是该线程的线程执⾏体。
public class MyRun implements Runnable {
@Override
public void run() {
// 线程要执行代码
for (int i = 0; i < 15; i++) {
// currentThread()方法获取到当前线程的对象
// 把上面的两行代码改成链式编程的写法
System.out.println(Thread.currentThread().getName() + " Holle world");
}
}
}
2. 创建 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。
3. 调⽤线程对象的 start() ⽅法来启动线程。
public class ThreadDemo {
public static void main(String[] args) {
// 创建MyRun的对象
// 表示多线程要执行的任务
MyRun mr = new MyRun();
// 创建线程对象,并且把任务传递给线程对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
//给线程设置名字
t1.setName("线程1");
t2.setName("线程2");
//开启线程
t1.start();
t2.start();
}
}
3、利用Callable接口和Future接口方式实现
Callable接口是Runable接口的增强版。同样用call()方法作为线程的执行体,增强了之前的run()方法。因为call方法可以有返回值,也可以声明抛出异常。
Future接口用来代表Callable接口里的call()方法的返回值,FutureTask类是Future接口的一个实现类,FutureTask类实现了RunnableFuture接口,而RunnnableFuture接口继承了Runnable和Future接口,所以说FutureTask是一个提供异步计算的结果的任务。
步骤如下:
1.创建一个类MyCallable实现Callable接口
2.重写call (是有返回值的,表示多线程运行的结果)
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int result = 0;
System.out.println(Thread.currentThread().getName() + " Holle world");
for (int i = 0; i < 15; i++) {
result += i;
}
return result;
}
}
3.创建MyCallable的对象(表示多线程要执行的任务)
4.创建FutureTask的对象(作用管理多线程运行的结果)
5.创建Thread类的对象,并启动(表示线程)
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建MyCallable的对象(表示多线程要执行的任务)
MyCallable mc = new MyCallable();
//创建FutureTask的对象(作用管理多线程运行的结果)
FutureTask<Integer> fu = new FutureTask<>(mc);
FutureTask<Integer> fu1 = new FutureTask<>(mc);
//创建线程的对象
Thread t1 = new Thread(fu);
Thread t2 = new Thread(fu1);
t1.setName("线程1");
t2.setName("线程2");
//启动线程
t1.start();
t2.start();
//获取多线程运行的结果
Integer result1 = fu.get();
Integer result2 = fu1.get();
System.out.println(result1 + " : " + result2);
}
}
4、使用线程池
在实际开发中,一般不使用以上三种方式,而是使用线程池的方式。
比如以下是阿里开发规范的相关规定:
线程池执行任务逻辑和线程池参数的关系
执行逻辑说明:
判断核心线程数是否已满,核心线程数大小和corePoolSize参数有关,未满则创建线程执行任务
若核心线程池已满,判断队列是否满,队列是否满和workQueue参数有关,若未满则加入队列中
若队列已满,判断线程池是否已满,线程池是否已满和maximumPoolSize参数有关,若未满创建线程执行任务
若线程池已满,则采用拒绝策略处理无法执执行的任务,拒绝策略和handler参数有关
Executors创建返回ThreadPoolExecutors对象
Executors创建返回ThreadPoolExecutor对象的方法共有三种:
Executors#newCachedThreadPool => 创建可缓存的线程池
Executors#newSingleThreadExecutor => 创建单线程的线程池
Executors#newFixedThreadPool => 创建固定长度的线程池
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
CompletionService<String> completionService = new ExecutorCompletionService<>(pool);
List<Future<String>> resultList = new ArrayList<>();
// for 循环
for (String param : list) {
Future<String> result = completionService.submit(()->{
// 业务逻辑
// 线程的结果
return "成功";
});
resultList.add(result);
}
pool.shutdown();
// 遍历线程的结果
List<String> list = new ArrayList<>();
for (Future<String> result : resultList) {
try {
System.out.println(result.get());
} catch (Exception e) {
e.printStackTrace();
}
}
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(() -> {
System.out.println("Asynchronous task");
});
executorService.shutdown();
ExecutorService的关闭
当我们使用完成ExecutorService之后应该关闭它,否则它里面的线程会一直处于运行状态。
当然以上方式也有弊端,我们再看一下阿里开发规范的相关规定:
因此,为了更好地控制和管理线程池,推荐使用ThreadPoolExecutor类来手动创建线程池,它可以自定义线程名称,还可以根据具体的需求自己设置线程池的参数:例如核心线程数、最大线程数、队列类型、线程工厂等、以及自定义拒绝策略来处理任务无法执行的情况。
实际上Executors本身只是一个工具类。它创建线程池的3种方式,都是是通过new 一个 ThreadPoolExecutor实现的:
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);
- corePoolSize:线程池中持久保持的核心线程数,可通过setCorePoolSize函数动态更改。
- runnableTaskQueue:用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue
- maximumPoolSize:线程池允许创建的最大线程数,可通过setMaximumPoolSize函数动态更改
- ThreadFactory:用于设置创建线程的工厂。
- RejectedExecutionHandler:当队列和线程池都满了,此时无法加入新的任务,线程池饱和。该参数表示饱和策略,默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略:AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy
- keepAliveTime:默认情况下是指非核心线程变成空闲状态后的存活时间,不影响核心线程,可通过setKeepAliveTime函数动态更改。++但是可通过调用allowCoreThreadTimeOut函数,来设置该变量对核心线程起作用。++
- TimeUnit:keepAliveTime的时间单位。
所以,针对可能出现的问题,自定义一个工具类来处理。
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 处理内容:线程池工具类
*/
public class ThreadPoolExecutorFactory {
/**
* corePoolSize 池中所保存的线程数,包括空闲线程。
*/
private static final int corePoolSize = 10;
/**
* maximumPoolSize - 池中允许的最大线程数(采用LinkedBlockingQueue时没有作用)。
*/
private static final int maximumPoolSize = 50;
/**
* keepAliveTime -当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间,线程池维护线程所允许的空闲时间
*/
private static final int keepAliveTime = 5;
/**
* 执行前用于保持任务的队列(缓冲队列)
*/
private static final int capacity = 3000;
/**
* 线程池对象
*/
private static ThreadPoolExecutor threadPoolExecutor = null;
//构造方法私有化
private ThreadPoolExecutorFactory(){}
public static ThreadPoolExecutor getThreadPoolExecutor(){
if(null == threadPoolExecutor){
ThreadPoolExecutor t;
synchronized (ThreadPoolExecutor.class) {
t = threadPoolExecutor;
if(null == t){
synchronized (ThreadPoolExecutor.class) {
t = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),new ThreadPoolExecutor.DiscardOldestPolicy());
}
threadPoolExecutor = t;
}
}
}
return threadPoolExecutor;
}
}
// 调用多线程工具类
FutureTaskUtil futureTaskUtil = new FutureTaskUtil(ThreadPoolExecutorFactory.getThreadPoolExecutor());
// 查询并返回业务数据
Future<Object> fu1 = futureTaskUtil.submit(() -> {
return baseService.getList(pd);
});
List<Map> = (List<Map>) futureTaskUtil.getResult(fu1);