并发编程 线程创建——五种方法
继承Thread类
示例如下:
public static void main(String[] args) {
System.out.println("主线程启动:"+Thread.currentThread().getName());
new Thread02().start();
}
public class Thread02 extends Thread {
@Override
public void run() {
System.out.println("子线程启动:"+Thread.currentThread().getName());
}
}
-
通过继承thread类实现的多线程 虽然实现的是
run
方法 但是要启动线程的话 应该是通过start
方法 而你启动了多少次start就相当于启动了多少个子线程run方法相当于只是指定了子线程中需要处理的事情 而真正的创建、启动线程并不是run方法
start方法也只是将线程调整到准备就绪的状态 cpu并不一定是立马执行 需要等待调度
-
run方法中的代码执行完毕后 线程就是死亡
-
不推荐使用 避免OOP单继承局限性
实现Runnable接口
示例如下:
public static void main(String[] args) {
// 通过lambda表达式的形式实现runnable接口 启动线程
new Thread(()->{System.out.println("子线程启动:"+Thread.currentThread().getName());}).start();
}
- 一般都是使用匿名内部类或者
lambda表达式
的形式实现runnable接口 实现新线程的启动 本质上thread类也是实现了runnable接口的 构造函数中也是可以接口runnable接口的 所以才支持这样的线程启动方式 - 推荐使用 避免了单继承的局限性
使用Callable和FutureTask创建线程
示例如下:
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 实现任务对象
FutureTask<String> task = new FutureTask<String>(() -> {
System.out.println("子线程启动" + Thread.currentThread().getName());
return "成功了";
});
// 将任务对象传递到新线程 启动
new Thread(task).start();
// 获取打印任务返回的结果
System.out.println(task.get());
}
- 与runnable接口不同 callable接口可以实现有返回值的线程创建 并且可以抛出异常
- 实现了callable接口任务 需要装载futuretask对象中 因为futuretask对象继承了runnable接口 所以可以通过futuretask对象来启动线程 多线程任务的返回结果可以通过futuretask获取
- 通过get获取线程任务的返回值 需要注意的是 get可能会造成阻塞 因为在get的时候是在等待线程任务完成 如果在get之前线程任务完成了 那么就不会造成阻塞
使用线程池创建线程
示例如下:
public static void main(String[] args) {
// region thread pool 实现方式
// 使用executors创建一个线程池
ExecutorService executorService = Executors.newCachedThreadPool();
// 线程池实现runnable接口启动线程
executorService.execute(() -> {
System.out.println("子线程启动:" + Thread.currentThread().getName());
});
// 线程池实现callable接口启动线程
Future<?> submit = executorService.submit(() -> {
System.out.println("子线程启动:" + Thread.currentThread().getName());
return "成功了";
});
// 还是通过futuretask获取返回值
System.out.println(submit.get());
// endregion
}
- 线程池的话 不用手动创建thread类 而是通过线程池的Executors框架来创建线程开启线程
- 使用Executors对象创建线程池 返回一个线程服务对象 通过线程服务对象 来操作和管理线程
- 线程池种类:
- newCachedThreadPool 创建一个
可缓存线程池
如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 - newFixedThreadPool 创建一个定长线程池
可控制线程最大并发数 超出的线程会在队列中等待 - newScheduledThreadPool 创建一个定长线程池
支持定时及周期性任务执行 - newSingleThreadExecutor 创建一个
单线程化的线程池
它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
- newCachedThreadPool 创建一个
spring @Async异步注解
示例如下:
@Async
public void springThread() throws InterruptedException {
Thread.sleep(3000);
System.out.println("子线程启动:"+Thread.currentThread().getName());
}
- 标记了@Async的方法在spring中将被认作异步方法 在调用的时候会开启新线程来处理
- spring的@async注解底层实现应该使用了线程池 这里不做过深的研究