目录
1.1 new ThreadPoolExecutor()中的参数
6 AbstractQueuedSynchronizer(AQS)
9 synchronized和ReentrantLock的区别
1 ThreadPoolExecutor
1.1 new ThreadPoolExecutor()中的参数
- int corePoolSize 核心线程数,核心线程会一直存活,即使没有任务需要执行
- int maximumPoolSize 最大线程数
- long keepAliveTime 线程空闲时间
- TimeUnit unit 指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效
- BlockingQueue<Runnable> workQueue 线程池中的任务队列
- ThreadFactory threadFactory 线程工厂,提供创建新线程的功能
- RejectedExecutionHandler handler 当线程池中的资源已经全部使用,添加新线程被拒绝时,会被调用
1.2 运作机理
1.2.1 使用有界队列
若有新的任务需要执行,如果线程池实际线程数小于核心线程数,则优先创建线程。
若大于核心线程数,则会将除了核心线程处理的任务之外剩下的任务加入队列。
若队列已满,则在剩余线程数(总线程数-核心线程数-等待队列数量)不大于最大线程数的前提下,创建新的非核心线程,处理完毕后等到达空闲时间后会被销毁。
若当前剩余线程数大于最大线程数,则执行拒绝策略,或其他自定义方式。
1.2.2 使用无界队列
与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。
当有新任务到来,系统的线程数小于核心线程数时,则新建线程执行任务。
当达到核心线程数后,就不会继续增加。
若后续仍有新的任务加入,而没有空闲的线程资源,则任务直接进入队列等待。
若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。
1.3 Executor拒绝策略
AbortPolicy:为Java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try-catch,否则程序会直接退出
DiscardPolicy:直接抛弃,任务不执行,空方法
DiscardOldestPolicy:从队列里面抛弃头部的一个任务,并再次执行此任务
CallerRunsPolicy:直接在 execute 方法的调用线程中运行被拒绝的任务。如果执行程序已关闭,则会丢弃该任务。
1.3.1 拒绝策略出现时机
- 当线程数已经达到最大线程数,且任务队列已满,会执行拒绝策略。
- 当线程池调用shutdown()方法后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()方法和线程池真正shutdown之间提交任务,会执行拒绝策略。
2 Java线程池
- newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
3 CountDownLatch
CountDownLatch用来实现类似于阻塞当前线程的功能,也就是说一个线程或多个线程一直等待,直到其他线程执行的操作完成。同时CountDownLatch是不可重用的,如果计数器减到0,则不能再次使用了。其关键点在于一个或多个线程等待其他线程的执行。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchTest {
private static CountDownLatch countDownLatch = new CountDownLatch(50);
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 50; i++) {
final int threadNum = i;
executor.execute(() -> {
try {
test(threadNum);
} catch (Exception e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await();
System.out.println("finish");
executor.shutdown();
}
private static void test(int threadNum) throws InterruptedException {
Thread.sleep(100);
System.out.println(threadNum);
Thread.sleep(100);
}
}
执行结果:
5
8
3
7
6
4
0
2
9
1
15
10
16
14
13
20
19
32
12
11
17
21
18
23
22
25
24
30
42
34
27
28
35
36
26
33
29
31
39
37
41
38
48
47
45
46
40
44
49
43
finish
4 Semaphore
Semaphore可以控制同时并发访问的线程个数。
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
private static Semaphore semaphore = new Semaphore(3);
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
final int threadNum = i;
executor.execute(() -> {
try {
//获取一个许可
semaphore.acquire();
test(threadNum);
//释放一个许可
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
private static void test(int threadNum) throws InterruptedException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " " + threadNum);
Thread.sleep(1000);
}
}
执行结果:
2019-04-20 18:33:33 1
2019-04-20 18:33:33 2
2019-04-20 18:33:33 0
2019-04-20 18:33:34 3
2019-04-20 18:33:34 4
2019-04-20 18:33:34 5
2019-04-20 18:33:35 7
2019-04-20 18:33:35 6
2019-04-20 18:33:35 8
2019-04-20 18:33:36 9
2019-04-20 18:33:36 10
2019-04-20 18:33:36 11
2019-04-20 18:33:37 14
2019-04-20 18:33:37 12
2019-04-20 18:33:37 13
2019-04-20 18:33:38 15
2019-04-20 18:33:38 17
2019-04-20 18:33:38 16
2019-04-20 18:33:39 19
2019-04-20 18:33:39 18
5 CyclicBarrier
CyclicBarrier允许一组线程相互等待,直到到达一个屏障点。通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。跟CountDownLatch不一样的是,CyclicBarrier是可以重用的。其关键点在于一个或多个线程之间互相等待。
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierTest {
private static CyclicBarrier barrier = new CyclicBarrier(5);
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int threadNum = i;
Thread.sleep(1000);
executor.execute(() -> {
try {
race(threadNum);
} catch (Exception e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
private static void race(int threadNum) throws InterruptedException, BrokenBarrierException {
Thread.sleep(1000);
System.out.println(threadNum + " is ready");
barrier.await();
System.out.println(threadNum + " continue");
}
}
执行结果:
0 is ready
1 is ready
2 is ready
3 is ready
4 is ready
4 continue
0 continue
2 continue
1 continue
3 continue
5 is ready
6 is ready
7 is ready
8 is ready
9 is ready
9 continue
5 continue
6 continue
7 continue
8 continue
6 AbstractQueuedSynchronizer(AQS)
不管是CountDownLatch、Semaphore、CyclicBarrier还是ReentrantLock,其内部都是用AQS来实现的。
AQS是使用Node实现的FIFO队列,同时维护了一个volatile int state(代表共享资源)的状态变量,可以用于构建锁或者其他同步装置的基础框架。其设计是基于模板方法模式来构建,可以实现它的排它锁和共享锁两种模式,使用时需要继承该类并通过实现它的方法管理其状态(tryAcquire()和tryRelease()、tryAcquireShared()和tryReleaseShared())。
7 创建线程的方式有哪几种
- 继承Thread类
- 实现Runnable接口
- 使用Callable和Future
8 线程的几种状态
- 新建(New) 创建后尚未启动的线程处于这种状态。
- 运行(Runable) Runable包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。
- 无限期等待(Waiting) 处于这种状态的线程不会被分配CPU执行时间,它们要等待被其他线程显式地唤醒。以下方法会让线程陷入无限期的等待状态:
- 没有设置Timeout参数的Object.wait()方法。
- 没有设置Timeout参数的Thread.join()方法。
- LockSupport.park()方法。
- 限期等待(Timed Waiting) 处于这种状态的线程也不会被分配CPU执行时间,不过无需等待被其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒。以下方法会让线程进入限期等待状态:
- Thread.sleep()方法。
- 设置了Timeout参数的Object.wait()方法。
- 设置了Timeout参数的Thread.join()方法。
- LockSupport.parkNanos()方法。
- LockSupport.parkUntil()方法。
- 阻塞(Blocked) 线程被阻塞了,“阻塞状态”与“等待状态”的区别是:“阻塞状态”在等待着获取到一个排他锁,这个事件将在另外一个线程放弃这个锁的时候发生;而“等待状态”则是在等待一段时间,或者唤醒动作的发生。在程序等待进入同步区域的时候,线程将进入这种状态。
- 结束(Terminated) 已终止线程的线程状态,线程已经结束执行。
9 synchronized和ReentrantLock的区别
- ReentrantLock表现为API层面的互斥锁(lock()和unlock()方法配合try/finally语句块来完成),而synchronized表现为原生语法层面的互斥锁。
- 相比synchronized,ReentrantLock增加了一些高级功能:等待可中断、可实现公平锁,以及锁可以绑定多个条件。
- 需要注意的是,性能因素不再是选择ReentrantLock的理由。在JDK1.5中,多线程环境下synchronized的吞吐量下降得非常严重,而ReentrantLock则能基本保持在同一个比较稳定的水平上。而在后续的JDK版本中,synchronized不断被优化,JDK1.6发布之后,synchronized和ReentrantLock的性能基本上是完全持平了。
10 volatile的作用
11 sleep方法和wait方法的区别
-
sleep是Thread类的静态方法,而wait是Object的实例方法。
-
在调用sleep方法的时候,不会释放锁资源,而wait方法会释放锁资源。
-
wait方法需要在同步语句块中使用,而sleep不需要。