线程的四种初始化方式
1、继承Thread类
2、实现Runnable接口
3、Callable接口
4、线程池
继承Thread类
继承Thread类复写父类中的run()。Thread类也是Runnable接口的一个子类。要想启动线程必须依靠Threan类的start()方法执行,线程启动之后会默认调用run()方法,一个线程只能启动唯一一次,多次启动会抛异常
public class ThreadDemo {
public static void main(String[] args) {
Thread myThread1 = new MyThread(); // 创建一个新的线程 myThread1 此线程进入新建状态
Thread myThread2 = new MyThread(); // 创建一个新的线程 myThread2 此线程进入新建状态
myThread1.start(); // 调用start()方法使得线程进入就绪状态
myThread2.start(); // 调用start()方法使得线程进入就绪状态
}
}
class MyThread extends Thread {
private int i = 0;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
实现Runable接口
因为Java是单继承的,一个类只能有一个父类,所以如果已经继承了一个类就不能在继承Thread类了。所以这时候启动线程可以用实现Runnable接口来完成。
public class ThreadDemo {
public static void main(String[] args) {
Runnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
}
}
class MyRunnable implements Runnable {
private int i = 0;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
实现Callable接口
实现Callable接口相对于实现Runnable接口它有返回值,而且还可以抛出异常,执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
public class ThreadDemo {
public static void main(String[] args) {
// 创建MyCallable对象
Callable<Integer> myCallable = new MyCallable();
//使用FutureTask来包装MyCallable对象
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable);
//FutureTask对象作为Thread对象的target创建新的线程
new Thread(ft).start();
System.out.println("主线程for循环执行完毕..");
try {
//取得新创建的新线程中的call()方法返回的结果
// get方法会阻塞,一直等待call()方法执行完后才会执行。
int sum = ft.get();
System.out.println("sum = " + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<Integer> {
private int i = 0;
@Override
public Integer call() {
int sum = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
线程池
在线程池中维护一个稳定的线程数量,有任务需要执行就到线程池中申请一个线程去执行,可以达到资源的控制。
public class ThreadDemo {
// 创建一个线程池
static ExecutorService executorService = Executors.newFixedThreadPool(5);
public static void main(String[] args) throws Exception {
// 提交给线程池执行一个任务
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("ThreadDemo.run");;
}
});
}
}
class MyCallable implements Callable<Integer> {
private int i = 0;
@Override
public Integer call() {
int sum = 0;
for (; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
线程池的7个核心参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁。线程池初始化后就已经有的线程数量。
maximumPoolSize:线程池最大线程数量
一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
keepAliveTime:空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定。销毁线程的数量为=maximumPoolSize-corePoolSize
unit: 空闲线程存活时间单位
workQueue:核心线程用完了,然后把新的任务压到队列中。
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
threadFactory:创建一个新线程时使用的工厂,可以用来设定线程名
handler:当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略。
①CallerRunsPolicy
在调用者线程中直接同步执行任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
②AbortPolicy
直接丢弃任务,并抛出RejectedExecutionException异常。
③DiscardPolicy
直接丢弃任务,什么都不做。
④DiscardOldestPolicy
抛弃进入队列最早的那个任务(抛弃队列的头部任务),然后尝试把这次拒绝的任务放入队列
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
5, // 核心线程数
20, // 最大线程数
5, // 空闲线程回收的时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue(20), // 队列
Executors.defaultThreadFactory(), // 工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
使用线程池的好处
- 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度:当任务到达时,可以不需要等待线程创建就能立即执行。
- 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,监控和调优。
异步编程
模拟用户下单操作。。。
1、根据地址id查询地址信息 – > 0.5s
2、查询用户购物车的数据 – >0.5s
3、查询购物车中的商品信息 – > 1s
4、创建订单 – > 0.5s
6、创建订单详情 --> 0.5s
用户创建要给一个订单总共耗时3s,没给操作都是同步执行的。如果变成异步是否会提高性能?
CompletableFuture
Java8新增了
CompletableFuture
提供对异步计算的支持,可以通过回调的方式处理计算结果。
runAsync 和 supplyAsync方法
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
CompletableFuture 提供了四个静态方法来创建一个异步操作。
没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下所有的方法都类同。
- runAsync方法不支持返回值。
- supplyAsync可以支持返回值。
使用默认和指定的线程池
// 创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
System.out.println("线程开始。。。");
// 使用默认的线程池
CompletableFuture.runAsync(() -> {
System.out.println("任务开始执行。。");
});
// 使用指定的线程池
CompletableFuture.runAsync(() ->{
System.out.println("任务2开始执行。。。");
},executorService);
System.out.println("线程结束");
线程执行完后有返回值
// 创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
System.out.println("线程开始。。。");
// 使用指定线程池运行一个任务
CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
Integer sum = 0;
for (int i = 1; i <=5; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
sum += i;
}
return sum;
}, executorService);
// get()阻塞等结果
System.out.println("sum:"+voidCompletableFuture.get());
System.out.println("线程结束");
计算结果完成时的回调方法
当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action。主要是下面的方法:
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
可以看到Action的类型是BiConsumer<? super T,? super Throwable>它可以处理正常的计算结果,或者异常情况。
whenComplete 和 whenCompleteAsync 的区别:
whenComplete:是执行当前任务的线程继续执行 whenComplete 的任务。
whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。exceptionally:任务运行出现异常后调用,在这个方法里面可以改变任务的返回值(降级)。
CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务线程:"+Thread.currentThread().getName());
Integer sum = 0;
for (int i = 1; i <= 5; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
int x = 10 / 0;
sum += i;
}
return sum;
}, executorService).whenComplete((resp, exc) -> {
// System.out.println("任务执行完了,返回结果是:" + resp + ",异常:" + exc);
System.out.println("whenComplete:"+Thread.currentThread().getName());
}).whenCompleteAsync((resp,exc)->{
System.out.println("whenCompleteAsync:"+Thread.currentThread().getName());
},executorService).exceptionally((exc) -> {
// System.out.println("出现异常了,这个方法里面可以修改返回结果");
return 20;
});
System.out.println("sum:" + integerCompletableFuture.get());
handle
handle 是执行任务完成时对结果的处理。
exceptionally和handle同时存在,handle 的返回值会覆盖掉exceptionally的。whenCompleteAsync是感知任务执行完了,而handle是任务执行完了真真的调用
CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务线程:"+Thread.currentThread().getName());
Integer sum = 0;
for (int i = 1; i <= 5; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
int x = 10 / 0;
sum += i;
}
return sum;
}, executorService).handle((resp,exc)->{
System.out.println("handle:resp:"+resp+",exc:"+exc);
return 22;
});
System.out.println("sum:" + integerCompletableFuture.get());
任务串行化
前一个任务执行完才能执行后一个任务。
// 不依赖前面任务的执行结果,只要前面任务执行完了,该任务就马上执行
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)
// 接收前一个任务的执行结果,并消费处理,该任务没有返回结果
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)
//接收前一个任务的执行结果,并消费,该任务有返回结果
public <U> CompletableFuture<U> thenApply( Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync( Function<? super T,? extends U> fn, Executor executor)
CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1开始执行,计算结果返回");
int x = 10 * 2;
return x;
}, executorService);
integerCompletableFuture.thenRunAsync(() -> {
System.out.println("任务2开始执行,这里不需要依赖任务1的返回值");
}, executorService);
integerCompletableFuture.thenAcceptAsync((resp)->{
System.out.println("任务3开始执行,这里需要依赖到任务1中的返回值:"+resp+",该任务没有返回值");
},executorService);
CompletableFuture<String> stringCompletableFuture = integerCompletableFuture.thenApplyAsync((resp) -> {
System.out.println("任务4开始执行,这里需要依赖到任务1中的返回值:" + resp + ",该任务有回值");
return "任务4:" + resp;
}, executorService);
System.out.println("sum:" + stringCompletableFuture.get());
两个任务组合-- 都要完成
两个任务都要完成以后,再去触发第三个任务。
// 组合两个任务,不需要获取前两个任务的结果,之前前两个任务执行完后就执行该任务
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action,Executor executor)
// 组合两个任务,需要前两个任务的返回结果,执行该任务后没有返回结果
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor)
// 组合两个任务,需要前两个任务的返回结果,执行该任务后有返回结果
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor)
// 第1个任务
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1执行");
return 10;
}, executorService);
// 第2个任务
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2执行");
return "java";
}, executorService);
// 组合前两个任务
// future1.runAfterBothAsync(future2, () -> {
// System.out.println("前两个任务执行后在执行该任务,不需要前两个任务的结果,该任务不需要返回");
// }, executorService);
// future1.thenAcceptBothAsync(future2, (f1, f2) -> {
// System.out.println("前两个任务执行后在执行该个任务,需要前两个任务的结果:" + f1 + " -- " + f2 + ",该任务不需要返回");
// }, executorService);
CompletableFuture<String> stringCompletableFuture = future1.thenCombineAsync(future2, (f1, f2) -> {
System.out.println("前两个任务执行后在执行该个任务,需要前两个任务的结果:" + f1 + " -- " + f2 + ",该任务需要返回");
return f1 + f2;
}, executorService);
System.out.println("sum:" + stringCompletableFuture.get());
多任务组合
allof顾名思义,就是所有的任务执行完成后返回future,
anyif就是只要有一个任务执行完成后就返回future并将第一个完成的参数带着一起返回,
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000);
System.out.println("任务1查询用户地址");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "addressList";
}, executorService);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2查询用户购物车");
return "carList";
}, executorService);
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务3查询购物车商品");
return "goodsList";
}, executorService);
// 组合多个任务,所有任务都要完成
// CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(future1, future2, future3);
// voidCompletableFuture.get(); // 等待所有任务完成。
// 组合多个任务,只要有一个任务完成
CompletableFuture<Object> objectCompletableFuture = CompletableFuture.anyOf(future1, future2, future3);
objectCompletableFuture.get(); // 等待所有任务完成。
// System.out.println("显示地址:" + future1.get());
// System.out.println("显示购物车:" + future2.get());
// System.out.println("显示商品:" + future3.get());