彻底学会Java并发编程——并发工具

31 阅读3分钟

线程池

线程池:创建一堆线程重复利用。

自定义线程池:

结构如下:

Blocking queue:5个变量3个方法

take:

put:

Size:

带超时的阻塞获取:

线程池的代码:

执行任务的操作:

带超时时间的阻塞添加

ThreadPoolExecutor

ThreadPoolExecutor的构造方法:

34是决定救急线程的

jdk提供的拒绝策略

当线程数大于到达maximumpoolsize后仍然有新任务。

根据这个构造方法,JDK executor类提供众多工厂方法来创建各种用途的线程池。

1 固定大小的线程池

固定大小线程池的实现:

大小是2,有三个任务

没有任务了之后也不会主动结束。

第二个参数是线程工厂,可以给线程改名字,如上所示:

2 带缓冲功能的线程池

意思是取走了后才能放东西,take后才能put

3 单线程线程池

就算线程执行失败,他还重新创造线程来执行别的任务。

提交任务

submit

pool.submit()提交任务后,主线程不会等待 Lambda 表达式中的任务执行完(包括休眠的 1000 毫秒 ),而是继续往下执行Log.debug("{}", future.get())这行代码。而future.get()方法会阻塞主线程,直到任务执行完毕并返回结果,这样就实现了任务的异步执行,同时又能获取执行结果。

任务调度线程池ScheduledThreadPoolExecutor

任务调度线程池之前用java.util.timer来实现定时功能。

timer的缺点:所有的任务由一个线程来调度,串行执行,如果一个任务延迟或者出现了异常会影响其他任务的执行。

ScheduledThreadPoolExecutor可以延时执行任务,而且可以弥补上述缺点,需要设置不同的线程数量。

定时执行任务:scheduleAtFixedRate()period参数设定是多久执行一次任务

scheduleWithFixedDelay()TimeUnit参数设定的是任务与任务之间的间隔时间是多少。

正确处理线程池异常:

第一种方法是主动加try catch块,第二种方法是用callable,future可以或者任务执行过程中的异常。

Callable 的 call 方法可以有返回值并且可以抛出异常。而 Future 用于获取 Callable 任务的执行结果,也可以通过 Future 的 get () 方法获取任务执行过程中抛出的异常。如果任务执行过程中抛出异常,在调用 Future 的 get () 方法时,这个异常会被重新抛出,可以在调用处进行捕获和处理。

线程池的应用:让每周四的18:00:00定时执行任务

Tomcat线程池

连接器部分用到了线程池。

  • LimitLatch 用来限流,可以控制最大连接个数,类似 J.U.C 中的 Semaphore 后面再讲
  • Acceptor 只负责【接收新的 socket 连接】
  • Poller 只负责监听 socket channel 是否有【可读的 I/O 事件】
  • 一旦可读,封装一个任务对象(socketProcessor),提交给 Executor 线程池处理
  • Executor 线程池中的工作线程最终负责【处理请求】

J.U.C

AQS (AbstractQueuedSynchronizer)

阻塞式锁和相关的同步器工具的框架

独占锁要重写tryAcquire、tryRelease、isHeldExclusively方法

重写tryAcquire方法。

ReentrantLock

默认为非公平锁实现

非公平锁实现原理:CAS操作成功该线程就直接获得锁,锁被释放的时候也是公平竞争锁。

可重入的原理:state0-1是获得了锁,可重入就是state可以继续加,当该线程再次获取同一把锁时,检查当前持有锁的线程是否为自己,直到state为0的时候才释放锁。

可打断的原理

公平锁:

条件变量await

条件变量signal