异步与线程池
1、初始化线程的4种方式
1)、继承Thread
2)、实现Runnable接口
3)、实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)
4)、线程池
方式1和方式2:主进程无法获取线程的运算结果。不适合当前场景。
方式3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致服务器资源耗尽。
方式4:通过如下两种方式初始化线程池
Executors.newFiexed ThreadPool(3);
//或者
new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit unit, workQueue,threadFactory,handler);
方式1、继续Thread
/**
* @description: 继承Thread
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/3/10 21:06
* @version: 1.0
*/
public class MyThread {
public static void main(String[] args) {
System.out.println("启动main方法开始");
OneMyThread oneMyThread = new OneMyThread();
oneMyThread.start();
System.out.println("结束main方法");
}
public static class OneMyThread extends Thread{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
}
}
}
运行结果:
启动main方法开始
结束main方法
当前线程:22
运行结果:5
方式2、实现Runable接口
/**
* @description: 实现Runnable接口
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/3/10 21:06
* @version: 1.0
*/
public class MyThread {
public static void main(String[] args) {
System.out.println("启动main方法开始");
TwoMyThread twoMyThread = new TwoMyThread();
new Thread(twoMyThread).start();
System.out.println("结束main方法");
}
/**
* 实现Runnable接口
*/
public static class TwoMyThread implements Runnable {
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
}
}
}
运行结果:
启动main方法开始
结束main方法
当前线程:22
运行结果:5
方式3、实现Callable泛型接口
/**
* @description: 继承Thread
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/3/10 21:06
* @version: 1.0
*/
public class MyThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("启动main方法开始");
FutureTask<Integer> futureTask = new FutureTask<>(new ThreeMyThread());
new Thread(futureTask).start();
//等待线程执行完成,返回结果
Integer i = futureTask.get();
System.out.println("线程执行完,返回结果:"+i);
System.out.println("结束main方法");
}
/**
* 实现Callable<T>泛型接口
*/
public static class ThreeMyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
return i;
}
}
}
运行结果:
启动main方法开始
当前线程:22
运行结果:5
线程执行完,返回结果:5
结束main方法
注意:结果的顺序,可以看到是一个阻塞等待
通过FutureTask类源码可以看到,FutureTask不仅可以接受Callable还可以接收Runnable.
FutureTask源码如下:
public class FutureTask<V> implements RunnableFuture<V> {
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
* @param result the result to return on successful completion. If
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future<?> f = new FutureTask<Void>(runnable, null)}
* @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
FutureTask实现RunnableFuture接口:RunnableFuture最终是继承的Runnable。
package java.util.concurrent;
/**
* A {@link Future} that is {@link Runnable}. Successful execution of
* the {@code run} method causes completion of the {@code Future}
* and allows access to its results.
* @see FutureTask
* @see Executor
* @since 1.6
* @author Doug Lea
* @param <V> The result type returned by this Future's {@code get} method
*/
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
方式4、直接提交线程池,线程池会自动开启任务。
每次通过new Thread()创建线程问题:导致资源耗尽。以上三种启动方式都不用。应该将所有异步多线程任务交给线程池执行。
线程池:
package com.yanxizhu.family.booking;
import java.util.concurrent.*;
/**
* @description: 继承Thread
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/3/10 21:06
* @version: 1.0
*/
public class MyThread {
//应保证,当前系统中线程池只有一两个,每个异步任务,提交给线程池自己执行。
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("启动main方法开始");
executorService.submit(new TwoMyThread()); //有返回值
//每个异步任务,提交给线程池自己执行。
executorService.execute(new TwoMyThread());//无返回值
System.out.println("结束main方法");
}
/**
* 实现Runnable接口
*/
public static class TwoMyThread implements Runnable {
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
}
}
}
运行结果:
启动main方法开始
结束main方法
当前线程:22
运行结果:5
使用线程池带来的好处:
1、降低资源的消耗
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
2、提高响应速度
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
3、提高线程的可管理性
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
总结区别
1、直接继承Thread、实现Runnable接口:不能直接得到返回值。
2、实现Callable泛型接口:可以得到返回值。
3、继承Thread、实现Runnable接口、实现Callable泛型接口,都不能达到控制资源的效果。
4、只有线程池可以控制资源,性能稳定。
评论 (0)