1.线程的实现方式
1.线程实现
1.1继承Thread
public class ThreadDemo {
public static void main(String[] args) {
ThreadExtends threadExtends = new ThreadExtends();
threadExtends.start();
}
}
class ThreadExtends extends Thread{
@Override
public void run() {
System.out.println("当前线程=="+Thread.currentThread().getName());
}
}
1.2实现Runnable接口
public class RunnableDemo {
public static void main(String[] args) {
RunnableDemo01 runnableDemo01 = new RunnableDemo01();
new Thread(runnableDemo01).start();
/*new Thread(()->{
System.out.println("当前线程:=="+Thread.currentThread().getName());
}).start();*/
}
}
class RunnableDemo01 implements Runnable{
@Override
public void run() {
System.out.println("当前线程:=="+Thread.currentThread().getName());
}
}
1.3实现callable
public class CallableDemo {
public static void main(String[] args) throws Exception {
CallableDemo01 callableDemo01 = new CallableDemo01();
FutureTask task = new FutureTask(callableDemo01);
new Thread(task).start();
Object o = task.get();
System.out.println("o = " + o);
}
}
class CallableDemo01 implements Callable<Object>{
@Override
public Object call() throws Exception {
System.out.println("当前线程:=="+Thread.currentThread().getName());
return 12;
}
}
2.线程池实现
详解请看下面线程池
3.获取线程的区别
通过上面的介绍我们发现获取线程的方式
- 继承Thread对象
- 实现Runnable接口
- 实现Callable接口
- 线程池
继承Thread对象和实现Runnable接口没有办法获取返回结果的,实现Callable接口可以获取线程的返回结果。当然这三种方式都不能控制我们的资源,线程池可以控制资源。
4.线程的几种状态
线程共包括以下 5 种状态:
-
新建状态(New): 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
-
就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
-
运行状态(Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
-
阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
- (01) 等待阻塞 – 通过调用线程的wait()方法,让线程等待某工作的完成。
- (02) 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
- (03) 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
2.线程池详解
1.线程池参数介绍
- corePoolSize 线程池核心线程大小
线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。任务提交到线程池后,首先会检查当前线程数是否达到了corePoolSize,如果没有达到的话,则会创建一个新线程来处理这个任务
- maximumPoolSize 线程池最大线程数量
当前线程数达到corePoolSize后,如果继续有任务被提交到线程池,会将任务缓存到工作队列中。如果队列也已满,则会去创建一个新线程来。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定
3.keepAliveTime 空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定
- unit 空闲线程存活时间单位
keepAliveTime的计量单位
TimeUnit是一个枚举类型:
NANOSECONDS : 1微毫秒,
MICROSECONDS:1微秒
MILLSECONDS:1毫秒
SECONDS:1秒
MINUTES:1分
HOURS:1小时
DAYS:1天
5.workQueue 工作队列
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
- ①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。- ②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到maxPoolSize(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。- ③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。- ④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
6.threadFactory 线程工厂
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
- handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:
- ①CallerRunsPolicy
该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
- ②AbortPolicy
该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
- ③DiscardPolicy
该策略下,直接丢弃任务,什么都不做。
- ④DiscardOldestPolicy
该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
2.常用的四种线程池
1.newCachedThreadPool (可缓存线程池)
核心线程数为0,总线程数是最大整数,当需要执行很多短时任务时它的使用率比较高,会显著提升性能。并且线程空闲60s就会被回收,所以如果没有任务,它并不会占用很多资源。
2.newFixedThreadPool(指定工作线程数量的线程池)
核心线程数等于最大线程数。也就是该线程池中没有非核心线程。
3.newSingleThreadPool(单线程化的线程池)
只有一个核心线程,所有任务按照先来先执行的顺序执行。
4.newScheduledThreadPool(定长线程池)
这个线程池指定了核心线程的数量,线程总数可以创建整数的最大数个。
该线程池支持定时以及周期性任务执行。
3.线程池执行顺序
4. 线程池核心数与最大线程数设置
核心线程
CPU密集型:核心线程数=CPU核心数(或 核心线程数=CPU核心数+1)
I/O密集型:核心线程数=2*CPU核心数(或 核心线程数=CPU核心数/(1-阻塞系数))
混合型:核心线程数=(线程等待时间/线程CPU时间+1)*CPU核心数
最大线程
IO密集型经验应用,最大线程设置为 2N+1 (N为CPU数量,下同)
CPU密集型应用,最大线程设置为 N+1
3.异步处理
1.ComplatableFuture介绍
Future是Java 5添加的类,用来描述一个异步计算的结果。你可以使用**
isDone
方法检查计算是否完成,或者使用get
阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel
方法停止任务的执行。
虽然Future
以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的CPU资源,而且也不能及时地得到计算结果,为什么不能用观察者设计模式当计算结果完成及时通知监听者呢?
在Java 8中, 新增加了一个包含50个方法左右的类: CompletableFuture,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。
CompletableFuture类实现了Future接口,所以你还是可以像以前一样通过get
方法阻塞或者轮询的方式获得结果,但是这种方式不推荐使用。
CompletableFuture和FutureTask同属于Future接口的实现类,都可以获取线程的执行结果。
2.创建异步对象
CompletableFuture 提供了四个静态方法来创建一个异步操作
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)
方法分为两类:
- runAsync 没有返回结果
- supplyAsync 有返回结果
public class CompletableFutureDemo01 {
public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
10L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws Exception{
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
Integer a = 100/50;
System.out.println("run当前线程:=="+Thread.currentThread().getName());
}, executor);
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
Integer a = 100 / 2;
System.out.println("supply当前线程:=="+Thread.currentThread().getName());
return a;
}, executor);
System.out.println("线程返回值:"+supplyAsync.get());
}
}
3.处理异常
当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);
public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) ;
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) ;
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) ;
相关方法的说明:
- whenComplete 可以获取异步任务的返回值和抛出的异常信息,但是不能修改返回结果
- execptionlly 当异步任务跑出了异常后会触发的方法,如果没有抛出异常该方法不会执行
- handle 可以获取异步任务的返回值和抛出的异常信息,而且可以显示的修改返回的结果
public class CompletableFutureDemo02 {
public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
10L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws Exception{
/*CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(()->{
Integer a = 100/20;
return a;
},executor).whenCompleteAsync((res,exc)->{
System.out.println("res:"+res);
System.out.println("exc:"+exc);
}).exceptionally(res->{//在异步任务显示的抛出了异常后才会触发的方法,没有异常不执行
System.out.println("res:"+res);
return 10;
});
System.out.println("返回结果:"+supplyAsync.get());*/
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
Integer a = 100 / 25;
return a;
}, executor).handleAsync((res,exc)->{//没有错误抛出,却能修改最终的返回结果
System.out.println("res:"+res);
System.out.println("exc:"+exc);
return res * 10;
});
System.out.println("结果:"+supplyAsync.get());
}
}
4. 线程串行
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)
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。
thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
thenRun方法:只要上面的任务执行完成,就开始执行thenRun,只是处理完任务后,执行 thenRun的后续操作
带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。
public class CompletableFutureDemo03 {
public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
10L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws Exception{
/*CompletableFuture<Integer> thenApply = CompletableFuture.supplyAsync(() -> {
Integer a = 20 / 10;
return a;
}, executor).thenApply(res -> {
return res * 5;
});
System.out.println("返回值:"+thenApply.get());*/
/*CompletableFuture<Void> thenAccept = CompletableFuture.supplyAsync(() -> {
Integer a = 100 / 4;
return a;
}, executor).thenAccept(res -> {
System.out.println("res:" + res * 10);
});*/
CompletableFuture<Void> thenRun = CompletableFuture.supplyAsync(() -> {
Integer a = 100 / 50;
return a;
}, executor).thenRunAsync(()-> {
Integer b = 200/4;
System.out.println("b"+b);
}, executor);
}
}
5.两个都完成
上面介绍的相关方法都是串行的执行,接下来看看需要等待两个任务执行完成后才会触发的几个方法
- thenCombine :可以获取前面两线程的返回结果,本身也有返回结果
- thenAcceptBoth:可以获取前面两线程的返回结果,本身没有返回结果
- runAfterBoth:不可以获取前面两线程的返回结果,本身也没有返回结果
public class CompletableFutureDemo04 {
public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
10L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws Exception{
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
Integer a = 10;
return a;
},executor);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
Integer b = 2;
return b;
}, executor);
// thenCombineAsync: 既可以获取前面两个线程的返回结果,同时也会返回结果给阻塞的线程
CompletableFuture<Integer> thenCombineAsync = future1.thenCombineAsync(future2, (f1, f2) -> {
return f1 + f2;
}, executor);
System.out.println("结果:"+thenCombineAsync.get());
// thenAcceptBothAsync 可以获取前面两个线程的返回结果,本身没有返回结果
CompletableFuture<Void> acceptBothAsync = future1.thenAcceptBothAsync(future2, (f1, f2) -> {
System.out.println("both:"+(f1+f2));
}, executor);
// runAfterBothAsync 不能获取前面两个线程的返回结果,本身也没有返回结果
CompletableFuture<Void> afterBothAsync = future1.runAfterBothAsync(future2, () -> {
System.out.println("afterBothAsync执行");
}, executor);
}
}
6.两个任务完成一个
两个任务只要有一个完成就会触发任务3的情况
- runAfterEither:不能获取完成的线程的返回结果,自身也没有返回结果
- acceptEither:可以获取线程的返回结果,自身没有返回结果
- applyToEither:既可以获取线程的返回结果,自身也有返回结果
public class CompletableFutureDemo05 {
public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
10L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws Exception{
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1开始了..."+Thread.currentThread().getName());
Integer a = 10 * 2;
System.out.println("任务1结束了..."+Thread.currentThread().getName());
return a;
}, executor);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2开始了..."+Thread.currentThread().getName());
Integer b = 100/4;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2结束了..."+Thread.currentThread().getName());
return b;
}, executor);
// runAfterEitherAsync 不能获取前面完成的线程的返回结果,自身也没有返回结果
future1.runAfterEitherAsync(future2,()->{
System.out.println("任务3开始了...");
},executor);
//可以获取线程的返回结果,自身没有返回结果
future1.acceptEitherAsync(future2,res->{
System.out.println("res:"+res);
System.out.println("任务3开始了...");
},executor);
//既可以获取线程的返回结果,自身也有返回结果
CompletableFuture<Integer> toEitherAsync = future1.applyToEitherAsync(future2, res -> {
Integer c = res * 10;
System.out.println("任务3开始了...");
return c;
}, executor);
System.out.println("结果:"+toEitherAsync.get());
}
}
7.多任务组合
allOf:等待所有任务完成
anyOf:只要有一个任务完成
public class CompletableFutureDemo06 {
public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
10L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws Exception{
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1 线程开始了..." + Thread.currentThread().getName());
int a = 100 / 5;
System.out.println("任务1 线程结束了..." + Thread.currentThread().getName());
return a;
}, executor);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2 线程开始了..." + Thread.currentThread().getName());
int b = 100 /10;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2 线程结束了..." + Thread.currentThread().getName());
return b;
}, executor);
CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务3 线程开始了..." + Thread.currentThread().getName());
int c = 100 /1;
System.out.println("任务3 线程结束了..." + Thread.currentThread().getName());
return c;
}, executor);
CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2, future3);
//阻塞,等待所有任务完成
allOf.get();
System.out.println("主任务执行完成..." + future1.get() + " :" + future2.get() + " :" + future3.get());
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2, future3);
System.out.println("主任务执行完成..." + anyOf.get());
}
}