new出来的线程都是用户线程,main方法也是用户线程,也叫主线程;
守护线程就是在后台隐藏的线程,例如垃圾回收;
public static void main(String[] args) {
Thread t1=new Thread(()->{
if(Thread.currentThread().isDaemon()){
System.out.println("t1是守护线程");
}else {
System.out.println("t1是用户线程");
}
while (true){
}
});
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main线程结束了");
}
我们可以看到main线程结束了,并不会关闭t1线程
可以对t1线程进行修改设置为守护线程
我们在设置守护线程的时候一定要注意
t1.setDaemon(true);要放在t1.start前面,否则就报下面的错误
可以看到,当main线程结束后,守护线程和jvm一起停止
我们来看下FutureTask,就是异步任务,当前线程执行的任务,不会干扰main方法中其他的代码
还可以正常往下走
在不需要返回值的情况可以使用Runnable接口
public class Producer {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//未来任务 当前线程 不需要返回值
FutureTask<String> futureTask= new FutureTask<String>(new MyThread(),null);
Thread t1=new Thread(futureTask);
t1.start();
System.out.println("主线程结束了");
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println("我是没有返回值的");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在需要获取返回值的时候,可以使用Callable接口
public class Producer {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//未来任务 当前线程 不需要返回值
FutureTask<String> futureTask= new FutureTask<String>(new MyThread());
Thread t1=new Thread(futureTask);
t1.start();
//如果获取返回值,当前线程阻塞,当前拿到结果之后,才能往下去执行其他的代码
String s = futureTask.get();
System.out.println("获取线程返回值:"+s);
System.out.println("主线程结束了");
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
return "我是有返回值的线程";
}
}
我们来看下同步方法的耗时
public class Producer {
public static void main(String[] args) throws ExecutionException, InterruptedException {
long start=System.currentTimeMillis();
aa();
bb();
cc();
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
public static void aa(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void bb(){
try {
Thread.sleep(1100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void cc(){
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
可以看到同步的时候耗时:3323
接下来我们看下异步
public static void main(String[] args) throws ExecutionException, InterruptedException {
//我们每次new线程的时候,都需要被垃圾回收多次,我们可以使用线程池来管理多个线程
//创建一个固定3个线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
long start=System.currentTimeMillis();
FutureTask f1=new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
Thread.sleep(1000);
return "f1";
}
});
FutureTask f2=new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
Thread.sleep(1100);
return "f2";
}
});
FutureTask f3=new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
Thread.sleep(1200);
return "f3";
}
});
executorService.submit(f1);
executorService.submit(f2);
executorService.submit(f3);
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
//关闭线程池
executorService.shutdown();
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
可以看到耗时:1214,比同步的要快很多
但是FutureTask有一个缺点就是,get方法会造成阻塞,导致无法往下执行;
一般都是把.get方法放到最后一行去处理
我们还可以有其他的方法来解决
3秒内拿不到结果,强行停止线程
.get(3,TimeUnit.SECONDS)
我们在来看下轮询的效果
package com.example.client.entity;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.*;
public class Producer {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
//我们每次new线程的时候,都需要被垃圾回收多次,我们可以使用线程池来管理多个线程
//创建一个固定3个线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
long start=System.currentTimeMillis();
FutureTask f1=new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
Thread.sleep(1000);
return "f1";
}
});
FutureTask f2=new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
Thread.sleep(1100);
return "f2";
}
});
FutureTask f3=new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
System.out.println("你好");
Thread.sleep(2200);
return "f3";
}
});
executorService.submit(f1);
executorService.submit(f2);
executorService.submit(f3);
System.out.println(f1.get());
System.out.println(f2.get());
while (true){
if(f3.isDone()){
//如果线程执行完成了 退出循环
System.out.println(f3.get());
break;
}else {
//不要那么频繁的去访问 我们设置一个小的阻塞时间
Thread.sleep(500);
System.out.println("业务正在处理中,请稍后");
}
}
//关闭线程池
executorService.shutdown();
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
}
isDone就是线程执行完毕了,才调用获取返回值的方法
轮询获取,每次都给用户看到业务在处理中,不会让用户看不到效果
轮询非常占用cpu,而且不优雅
但是以上都不是我们想要的
我们来看下CompletableFuture(可完成的未来)
CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方
我们进入源码 可以看到
CompletableFuture实现了Future和 CompletionStage接口
Future有的功能,CompletableFuture都有
CompletionStage(完成阶段):表示异步计算过程中的某一个阶段,一个阶段完成后,可能会触发另外一个阶段
我们接下来先看下没有返回值的方法runAsync
他有2个参数,一个是Runnable,一个是线程池
我们先不加线程池看下什么效果
public static void main(String[] args) throws Exception {
CompletableFuture completableFuture=CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("返回值:"+completableFuture.get());
System.out.println("主线程结束");
}
我们可以看到没有返回值,并且不加线程池,线程的名字为ForkJoinPool.commonPool
我们加一下线程池看一下
public static void main(String[] args) throws Exception {
//创建3个固定的线程池
ExecutorService executors=Executors.newFixedThreadPool(3);
CompletableFuture completableFuture=CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},executors);
System.out.println("主线程结束");
}
我们可以看到加了线程池之后,线程的名字就变成了pool-1-thread-1
我们在来看下有返回值的方法
public static void main(String[] args) throws Exception {
//创建3个固定的线程池
// ExecutorService executors=Executors.newFixedThreadPool(3);
CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "aaaa";
});
System.out.println(completableFuture.get());
System.out.println("主线程结束");
}
可以看到有返回值的线程名称还是ForkJoinPool.commonPool
我们在看下加了线程池的效果
public static void main(String[] args) throws Exception {
//创建3个固定的线程池
ExecutorService executors=Executors.newFixedThreadPool(3);
CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "aaaa";
},executors);
System.out.println(completableFuture.get());
//关闭线程池
executors.shutdown();
System.out.println("主线程结束");
}
加了线程池之后线程池的名称变成了pool-1
但是上面还是有点不足,还会造成阻塞
接下来我们看下下面的代码
使用whenComplete方法在线程完成之后,回调处理返回结果,这样不会造成阻塞
使用exceptionally方法 在线程出错了,抛出异常
可以看到,并没有打印我们的返回结果,为啥呢?因为没有加线程池
接下来我们把线程池加上
public static void main(String[] args) throws Exception {
//创建3个固定的线程池
ExecutorService executors=Executors.newFixedThreadPool(3);
try {
CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "aaaa";
},executors).whenComplete((v,e)->{
//线程完成时的方法 进入回调接口
if(e==null){
//如果没有异常 打印 线程返回的结果
System.out.println("线程返回结果:"+v+",你在这里可以处理其他的方法了");
}
}).exceptionally((e)->{
//线程执行错误 抛出异常
e.printStackTrace();
return null;
});
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭线程池
executors.shutdown();
}
System.out.println("主线程结束");
}
可以看到先走了主线程,我业务的线程等处理完成了才打印,这样就解决了阻塞
join()和get()方法是一样的,区别就是join不需要在箭头位置抛出异常
而get需要抛出异常
BiConsumer<? super T, ? super Throwable>
这个表示2个参数,一个v返回结果,一个Throwable 异常
接下来我们在看下下面的例子
public class Producer {
public static void main(String[] args) throws Exception {
List<String>list=new ArrayList<>();
long start =System.currentTimeMillis();
list.add(aa());
list.add(bb());
list.add(cc());
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
System.out.println(list);
}
public static String aa() throws InterruptedException {
Thread.sleep(1000);
return "aa";
}
public static String bb() throws InterruptedException {
Thread.sleep(1000);
return "bb";
}
public static String cc() throws InterruptedException {
Thread.sleep(1000);
return "cc";
}
}
拿到3个方法的返回值,放入list集合中
耗时3秒
我们使用异步的方式,来改造下代码
public class Producer {
public static void main(String[] args) throws Exception {
List<String>list=new ArrayList<>();
//创建3个固定的线程池
ExecutorService executors=Executors.newFixedThreadPool(3);
long start =System.currentTimeMillis();
try {
CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
try {
return aa();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}, executors);
CompletableFuture<String> c2 = CompletableFuture.supplyAsync(() -> {
try {
return bb();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}, executors);
CompletableFuture<String> c3 = CompletableFuture.supplyAsync(() -> {
try {
return cc();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}, executors);
list.add(c1.get());
list.add(c2.get());
list.add(c3.get());
System.out.println(list);
}finally {
executors.shutdown();
}
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
public static String aa() throws InterruptedException {
Thread.sleep(1000);
return "aa";
}
public static String bb() throws InterruptedException {
Thread.sleep(1000);
return "bb";
}
public static String cc() throws InterruptedException {
Thread.sleep(1000);
return "cc";
}
}
可以看到耗时1秒
我们在看下thenApply(然后应用)这个方法
package com.example.client.entity;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.function.Function;
public class Producer {
public static void main(String[] args) throws Exception {
List<String>list=new ArrayList<>();
//创建3个固定的线程池
ExecutorService executors=Executors.newFixedThreadPool(3);
long start =System.currentTimeMillis();
try {
CompletableFuture<Integer> c1 = CompletableFuture.supplyAsync(() -> {
System.out.println("我是第一阶段");
return 1;
}, executors)
.thenApply(v->{
System.out.println("我是第二阶段");
return v+1;
})
.thenApply(v->{
System.out.println("我是第三阶段");
return v+1;
})
.whenComplete((v,e)->{
if(e==null){
System.out.println("我是第四阶段"+v);
}
})
.exceptionally(e->{
e.printStackTrace();
return null;
})
;
}finally {
executors.shutdown();
}
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
}
意思就是当前supplyAsync执行完成之后,我还可以执行多个thenApply方法
等执行thenApply完了之后,在执行whenComplete方法
我们可以看到,如果是报错了,就不会往下走了
我们在看下handle(处理程序)这个方法,和thenApply的效果是一样的
那么我们看一下报错,是什么效果
可以看到,报错之后,下面的handle方法还是正常的运行,最后走到异常的方法
和thenApply区别就是,thenApply不会往下执行,直接进去异常方法
thenAccept这个方法是然后接受,没有返回值
public static void main(String[] args) throws Exception {
//创建3个固定的线程池
ExecutorService executors=Executors.newFixedThreadPool(3);
long start =System.currentTimeMillis();
try {
CompletableFuture.supplyAsync(() -> {
System.out.println("我是第一阶段");
return 1;
}, executors)
.thenApply((v)->{
System.out.println("我是第二阶段");
return v+1;
})
.thenAccept(v->{
System.out.println("我是没有返回值的:"+v);
})
;
}finally {
executors.shutdown();
}
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
我们在来看下thenRun方法
public static void main(String[] args) throws Exception {
//创建3个固定的线程池
ExecutorService executors=Executors.newFixedThreadPool(3);
long start =System.currentTimeMillis();
try {
CompletableFuture<Void> aa = CompletableFuture.supplyAsync(() -> {
System.out.println("我是第一阶段");
return 1;
}, executors)
.thenRun(()->{
System.out.println("我是没有返回值,并且不需要依赖supplyAsync里面的结果");
}
);
System.out.println(aa.get());
}finally {
executors.shutdown();
}
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
当我们使用thenApplyAsync(然后应用同步)方法的时候
线程的名字和第一阶段的名字不一样
我们在来看下applyToEither方法,谁最快用谁
public static void main(String[] args) throws Exception {
CompletableFuture<String> aa=CompletableFuture.supplyAsync(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "aa";
});
CompletableFuture<String> bb=CompletableFuture.supplyAsync(()->{
return "bb";
});
Object o = aa.applyToEither(bb,x->{
return x+"最快";
}).get();
System.out.println(o);
}
可以看到bb最快返回的就是bb
我们在看下thenCombine,把2个返回的结果组合到一块
public static void main(String[] args) throws Exception {
CompletableFuture<String> aa=CompletableFuture.supplyAsync(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("aa执行完毕");
return "aa";
});
CompletableFuture<String> bb=CompletableFuture.supplyAsync(()->{
System.out.println("bb执行完毕");
return "bb";
});
CompletableFuture<String> cc = aa.thenCombine(bb, (x, y) -> {
return x + y;
});
System.out.println(cc.join());
}