多线程
4 多线程常见场景
- 客户端(移动端App端)开发
- 异步发送短信/邮件
- 执行比较耗时的代码改用多线程异步执行,提高接口的响应速度
- 异步写入日志(日志框架底层)
- 多线程下载
5 多线程的创建方式
5.1 继承Thread类
/**
* @ClassName ThreadDemo
* @Description 继承Thread
* @Author Fclever
* @Date 2021/12/16 11:12
**/
public class ThreadDemo extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"running");
}
public static void main(String[] args) {
// 创建线程
ThreadDemo threadDemo = new ThreadDemo();
// 启动线程 调用start()方法
// 执行start方法之后,线程进入就绪状态,等待cpu的调度,分配到时间片之后,进入到运行状态
threadDemo.start();
}
}
5.2 实现Runnable接口
/**
* @ClassName RunnableDemo
* @Description 实现Runnable接口
* @Author Fclever
* @Date 2021/12/16 11:20
**/
public class RunnableDemo implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"running");
}
public static void main(String[] args) {
// 创建Thread类,使用实现Runnable接口的类传参
Thread thread = new Thread(new RunnableDemo());
// 启动线程
thread.start();
}
}
5.3 使用匿名内部类
/**
* @ClassName AnonymousDemo
* @Description 匿名内部类形式
* @Author Fclever
* @Date 2021/12/16 11:32
**/
public class AnonymousDemo {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "running");
}
});
thread.start();
}
}
5.4 使用Lambda表达式
/**
* @ClassName LambdaDemo
* @Description
* @Author Fclever
* @Date 2021/12/16 14:20
**/
public class LambdaDemo {
public static void main(String[] args) {
// lambda表达式
Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getName() + "running"));
// 启动线程
thread.start();
}
}
5.5 使用Callable和Future
- 可以获取到线程的返回结果,底层使用JUC并发包的LockSupport实现
- 主线程开辟新线程,使用Callable获取返回键结果时,主线程会等待
- 之所以能实现这种阻塞效果,是因为FurureTask底层依赖了LockSupport
- 在调用get方法时,会将主线程阻塞,当子线程返回结果后,会重新调用方法释放主线程
/**
* @ClassName CallableDemo
* @Description Callable创建线程,主要特点是能够获取到线程的返回结果
* @Author Fclever
* @Date 2021/12/16 14:24
**/
public class CallableDemo implements Callable {
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName()+"running");
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
return 1;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建实现Callable接口的对象
CallableDemo callableDemo = new CallableDemo();
FutureTask futureTask = new FutureTask<>(callableDemo);
// 启动线程
Thread thread = new Thread(futureTask);
thread.start();
// 获取线程执行的返回值
Object rs = futureTask.get();
System.out.println("rs:"+rs);
System.out.println("接收到了返回结果,主线程继续执行!");
}
}
5.6 使用线程池(如Executor框架)
- 核心使用复用机制来维护线程
/**
* @ClassName ThreadPoolDemo
* @Description 线程池Demo,使用Executors
* @Author Fclever
* @Date 2021/12/16 14:37
**/
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"running");
}
});
}
}
5.7 Spring @Async异步注解
- @Async底层是通过代理实现的
- 注意点
- 需要在启动类上开启支持该异步注解
- @Async注解只能用于公共方法
- 方法需要是公共以便可以代理。
- 不可以直接在一个类中直接调用类内的异步方法
- “自我调用不起作用”,因为它绕过了代理,直接调用底层方法
- 需要在启动类上开启支持该异步注解
/**
* @ClassName AsyncDemo
* @Description Spring @Async注解
* @Author Fclever
* @Date 2021/12/16 14:44
**/
@RestController
@Slf4j
public class AsyncControllerDemo {
@RequestMapping("/test")
public int test() {
log.info("<1>");
int res = this.asyncLog();
log.info("<3>");
return 5;
}
@Async
public int asyncLog() {
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
log.info("<2>");
return 44;
}
}
5.8 自定义@Async异步注解
- 自定义注解
/**
* @ClassName FcAsync
* @Description
* @Author Fclever
* @Date 2021/12/17 14:14
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DiAsync {
/**
* 异步记录日志的方法名称
* @return
*/
String name() default "";
}
- AOP
/**
* @ClassName FcAsyncAOP
* @Description
* @Author Fclever
* @Date 2021/12/17 14:56
**/
@Aspect
@Component
@Slf4j
public class DiAsyncAOP {
/**
* 环绕通知
* @param joinPoint
*/
@Around(value = "@annotation(com.fc.multithread.diyThread.DiAsync)")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
log.info("环绕通知开始执行");
// 给目标方法开辟线程调用
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
joinPoint.proceed();
}
});
log.info("环绕通知结束执行");
} catch (Exception e) {
e.printStackTrace();
}
}
}
- Controller
/**
* @ClassName DiAsyncController
* @Description
* @Author Fclever
* @Date 2021/12/17 15:28
**/
@RestController
@Slf4j
public class DiAsyncController {
@Autowired
private DiAsyncService diAsyncService;
@RequestMapping("/diy")
public String diy() {
log.info("<1>");
diAsyncService.test();
log.info("<3>");
return "22";
}
}
- Service
/**
* @ClassName DiAsyncService
* @Description
* @Author Fclever
* @Date 2021/12/17 15:42
**/
@Component
@Slf4j
public class DiAsyncService {
@DiAsync(name = "test")
public void test() {
try {
log.info("目标方法执行,阻塞2s");
Thread.sleep(2000);
log.info("<2>");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Date 2021/12/17 15:42
**/
@Component
@Slf4j
public class DiAsyncService {
@DiAsync(name = "test")
public void test() {
try {
log.info("目标方法执行,阻塞2s");
Thread.sleep(2000);
log.info("<2>");
} catch (Exception e) {
e.printStackTrace();
}
}
}