文章目录
2. 多线程基础
多线程的创建方式
- 继承Thread类创建线程
- 实现Runnable接口创建线程
- 使用Callable和Future创建线程
- 使用线程池类似Executor框架
- spring @Async异步注解 结合线程池
2.1 继承Thread类创建线程
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("继承Thread类创建线程");
}
public static void main(String[] args) {
//创建线程
MyThread myThread = new MyThread();
//启动线程是start()而不是run()
myThread.start();
}
}
2.2 实现Runnable接口创建线程
public class MyThread implements Runnable{
@Override
public void run() {
System.out.println("实现Runnable接口创建线程");
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}
}
2.3 使用Callable和Future创建线程
2.3.1 基本使用
Callable和Future 线程可以获取到返回结果 底层基于LockSupport
从jdk1.5开始,Java提供了Callable接口,该接口是Runnable接口的增强版,Callable接口提供了一个call()方法,可以看作是线程的执行体,但call()方法比run()方法更强大。
call()方法可以有返回值。
call()方法可以声明抛出异常。
public class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
int i = 0;
for (; i < 100; i++){
System.out.println(Thread.currentThread().getName()+"的循环变量i"+i);
}
return "线程返回值"+i;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask<String> futureTask= new FutureTask<>(myThread);
new Thread(futureTask,"CallableThread").start();
for (int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName()+"的循环变量i"+i);
}
String s = futureTask.get();
System.out.println(s);
}
}
我们可以用FutureTask来包装Callable对象, 并通过get()获取线程返回值, 注意当获取线程返回值时如果Callable线程运行速度更慢会导致当前主线程阻塞, 原理就是FutureTask底层调用了LockSupport的park()和unpark()方法
2.3.2 手写 Callable和FutureTask模式
自定义Callable接口
@FunctionalInterface
public interface MyCallable <V>{
V call();
}
自定义FutureTask类
public class MyFutureTask<V> implements Runnable{
MyCallable<V> myCallable;
Object lock = new Object();
V call;
public MyFutureTask(MyCallable myCallable){
this.myCallable = myCallable;
}
@Override
public void run() {
call = myCallable.call();
synchronized (lock){
lock.notify();
}
}
public V get(){
synchronized (lock){
try {
//wait() 方法让当前线程(调用此方法线程)进入等待状态。
//直到其他线程调用此对象的 notify()方法或 notifyAll()方法
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return call;
}
}
使用
public static void main(String[] args){
MyFutureTask<Integer> task=new MyFutureTask<>((MyCallable<Integer>) () -> {
System.out.println(Thread.currentThread().getName()+"开始执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 5;
});
new Thread(task).start();
Integer integer = task.get();
System.out.println(integer);
}
2.4 使用线程池类似Executor框架
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(()-> System.out.println(Thread.currentThread().getName()));
}
2.5 spring @Async异步注解 结合线程池
2.5.1 基本使用
@Async("taskExecutor")
public void updateViewCount(ArticleMapper articleMapper,Article article){
Article articleUpdate = new Article();
articleUpdate.setViewCounts(article.getViewCounts() + 1);
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Article::getId,article.getId());
queryWrapper.eq(Article::getViewCounts,article.getViewCounts());
articleMapper.update(articleUpdate,queryWrapper);
try {
//睡眠5秒 证明不会影响主线程的使用
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
其本质就是在不加注解的情况下,在线程池中获取一个线程去调用updateViewCount方法
2.5.2 手写 @Async异步注解
定义注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAsync {
String name() default "";
}
定义切面类
@Component
@Aspect //切面 定义了通知和切点的关系
public class MyAsyncAspect {
@Around(value = "@annotation(com.example.demo.annotation.MyAsync)")
public void around(ProceedingJoinPoint joinPoint){
new Thread(()-> {
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
},"myAsync注解线程").start();
}
}