Java高级
内容正在完善中……线程池ing
1. Collection
List
-
ArrayList:数组实现,按下标访问效率高(随机访问),增删操作费事
-
Victor:在ArrayList基础上添加了线程安全特性,效率降低
-
LinkedList:双向链表实现,增删效率高,不支持下标访问(随机访问),只能顺序查找
Set
-
HashSet:哈希表实现,查找效率高 O(1),无序
-
LinkedHashSet:双向链表维护元素的插入顺序,查询效率高,有序
-
TreeSet:红黑树实现,有序( 甚至拿来排序orz ),查找效率较低 O(logN)
Queue
- 待补充
2. Map
1. HashMap( 面试必问 )
- 实现:哈希表
- 非线程安全
- 作用:存Key-Value类型的值,每一对形成一组Entry<K, V>
- 结构:传说中的拉链法(手写练习,加深印象orz
- 使用:
-
存值:
put(K key, V value)) -
通过key取value:
get(K key),返回value -
取所有keys:
keySet(),返回key的集合 -
遍历: (常考)
- KeySet
private static String keySetTest(HashMap<Integer, String> hashMap){ StringBuilder result = new StringBuilder("KeySetTest: "); for ( Integer i : hashMap.keySet() ){ result.append(i).append(": ").append(hashMap.get(i)); } return result.toString(); }- EntrySet
private static String entrySetTest(HashMap<Integer, String> hashMap){ StringBuilder result = new StringBuilder("EntrySetTest: "); for ( Map.Entry<Integer, String> entry: hashMap.entrySet() ){ result.append(entry.getKey()).append(": ").append(entry.getValue()); } return result.toString(); }- Iterator
private static String iteratorTest(HashMap<Integer, String> hashMap){ StringBuilder result = new StringBuilder("IteratorTest: "); Iterator iterator = hashMap.entrySet().iterator(); while( iterator.hasNext() ){ Map.Entry entry = (Map.Entry) iterator.next(); result.append(entry.getKey()).append(": ").append(entry.getValue()); } return result.toString(); }- ForeachIteratorTest(idea给上面的方法报warning,推荐了foreach写法,以下为自动修改后的方案)
注意:经检验这就是EntrySet一样的做法……idea会把此种方案视为可优化为foreach循环的可优化点
private static String foreachIteratorTest(HashMap<Integer, String> hashMap){ StringBuilder result = new StringBuilder("ForeachIteratorTest: "); for ( Map.Entry<Integer, String> entry: hashMap.entrySet() ) { result.append(entry.getKey()).append(": ").append(entry.getValue()); } return result.toString(); }- IteratorWithoutType
private static String iteratorWithoutTypeTest(HashMap<Integer, String> hashMap){ StringBuilder result = new StringBuilder("IteratorWithoutTypeTest: "); Iterator<Map.Entry<Integer, String>> iterator = hashMap.entrySet().iterator(); while( iterator.hasNext() ){ Map.Entry entry = iterator.next(); result.append(entry.getKey()).append(": ").append(entry.getValue()); } return result.toString(); }- ForeachIteratorWithoutType(同理为idea优化)
private static String foreachIteratorWithoutTypeTest(HashMap<Integer, String> hashMap){ StringBuilder result = new StringBuilder("ForeachIteratorWithoutTypeTest: "); for (Map.Entry entry : hashMap.entrySet()) { result.append(entry.getKey()).append(": ").append(entry.getValue()); } return result.toString(); } -
测速: 目前测速方案导致结果波动验证,仍在调整中……
- 测试类:
public static void main(String[] args){ HashMap<Integer, String> hashMap = new HashMap<>(); for (int i = 0; i < 1000000; i++) { hashMap.put(i, String.valueOf(i)); } for (int i = 1; i <= 6; i++) { System.out.println(runTestWithClock(i, hashMap).substring(0, 30)); } } private static String runTestWithClock(int n, HashMap<Integer, String> hashMap){ String result = null; long clock = System.currentTimeMillis(); long timer = 0; System.out.println("--------"+n+"-------"); switch (n){ case 1: result = keySetTest(hashMap); break; case 2: result = entrySetTest(hashMap); break; case 3: result = iteratorTest(hashMap); break; case 4: result = foreachIteratorTest(hashMap); break; case 5: result = coolerIteratorTest(hashMap);; break; case 6: result = foreachCoolerIteratorTest(hashMap); break; } timer = System.currentTimeMillis() - clock ; System.out.println("Timer: "+timer); return result; }- 运行结果:(安全起见运行了3次,结果排在Timer行)
--------1------- Timer: 1232 1234 1207 KeySetTest: 0: 01: 12: 23: 34: --------2------- Timer: 96 112 100 EntrySetTest0: 01: 12: 23: 34: --------3------- Timer: 127 120 109 IteratorTest: 0: 01: 12: 23: 3 --------4------- Timer: 98 91 111 ForeachIteratorTest: 0: 01: 12 --------5------- Timer: 112 99 104 CoolerIteratorTest: 0: 01: 12: --------6------- Timer: 105 106 122 ForeachCoolerIteratorTest: 0:- 结果统计:
method 1 2 3 KeySet 1232 1234 1207 EntrySet 96 112 100 Iterator 127 120 109 ForeachIterator 98 91 111 CoolerIterator 112 99 104 ForeachCoolerIterator 105 106 122 结果波动性比较大,安全起见重新制定测试计划
- 测试条件:①HashMap大小 ②测试次数 ③测试方法的顺序
先对①和②进行验证
-
| size | 2000k | 1500k | 1000k | 100k | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| times | 1 | 2 | 3 | 4 | 5 | 1 | 2 | 3 | 1 | 2 | 3 | 1 | 2 | 3 |
| KeySet | 2315 | 2271 | 2303 | 2328 | 2258 | 813 | 816 | 819 | 1232 | 1234 | 1207 | 25 | 30 | 25 |
| EntrySet | 203 | 1010 | 201 | 991 | 201 | 735 | 738 | 745 | 96 | 112 | 100 | 23 | 25 | 23 |
| Iterator | 178 | 155 | 165 | 165 | 170 | 180 | 173 | 188 | 127 | 120 | 109 | 24 | 26 | 24 |
| ForeachIterator | 986 | 147 | 983 | 194 | 971 | 138 | 134 | 137 | 98 | 91 | 111 | 27 | 27 | 37 |
| IteratorWithoutType | 185 | 187 | 179 | 162 | 169 | 134 | 122 | 130 | 112 | 99 | 104 | 18 | 19 | 19 |
| ForeachIteratorWithoutType | 183 | 188 | 180 | 156 | 150 | 145 | 112 | 121 | 105 | 106 | 122 | 19 | 21 | 20 |
表格渲染结果不佳,另附https://paste.ubuntu.com/p/VKmyxsGbdh/
-
由此表大致可以认为在此次测试中,HashMap<Integer,String>存储简单类型的k-v值:
- 在容量在100k级别时,各种方法差别并不大
- 在容量到1000k及以上的级别时,执行效率开始呈现较大差别
- KeySet方式是同数量级中最低效率的遍历方式
- Iterator方式效率相对较高
- 特殊情况
- KeySet方式在size为1500k时的效率明显异常,或许有特殊优化?(又去跑了几次:1869,844,787,的确超过1秒的很少,待调查* 存在③影响的可能)
- EntrySet和ForeachIterator在2000k时竟然在900ms和100ms之间交替orz,回过头一看,ForeachIterator经过idea优化之后竟然就是EntrySet的方法,测试方案需进行简化*
-
猜想:
- ForEach是通过Iterator实现的,不然在idea中不应该会推荐进行此优化,并且在运行时间上也存在相似之处
-
进一步推测:
- 不指定Entry类型的Iterator和不指定Entry类型的for-each循环 实现相似
- 指定Entry类型的Iterator和指定Entry类型的for-each循环 实现相似
-
验证:
- 通过反编译.class文件获得EntrySet测试的代码:
private static String entrySetTest(HashMap<Integer, String> hashMap) { StringBuilder result = new StringBuilder("EntrySetTest: "); Iterator var2 = hashMap.entrySet().iterator(); while(var2.hasNext()) { Entry<Integer, String> entry = (Entry)var2.next(); result.append(entry.getKey()).append(": ").append((String)entry.getValue()); } return result.toString(); }- 同理查看foreachIteratorTest
private static String foreachIteratorTest(HashMap<Integer, String> hashMap) { StringBuilder result = new StringBuilder("ForeachIteratorTest: "); Iterator var2 = hashMap.entrySet().iterator(); while(var2.hasNext()) { Entry<Integer, String> entry = (Entry)var2.next(); result.append(entry.getKey()).append(": ").append((String)entry.getValue()); } return result.toString(); }果然是一样的,这些for-each是基于iterator实现的
-
结论:
- size较大时,iterator进行map遍历时的效率最高,KeySet效率最低
- 相对来说,指定iterator的Entry类型可以增加遍历的效率
-
如何在高并发环境下使用?
- HashMap map = Collections.synchronizedMap(new HashMap(...));
- 使用ConcurrentHashMap
2. LinkedHashMap
- 双向链表来维护元素的顺序
- 顺序:插入顺序或者LRU(Least recently used)顺序
3. HashTable
- 线程安全√
- 不推荐使用,仅作了解,官方文档推荐高并发场景使用→ConcurrentHashMap进行替代
Thread
基础使用
- 实现 Runnable 接口
- 常用且高效
public class RunnableThreadTest implements Runnable{
@Override
public void run() {
// do sth.
}
}
运行:
public static void main(String[] args) {
RunnableThreadTest threadTest = new RunnableThreadTest();
Thread thread = new Thread(threadTest);
thread.start();
}}
- 继承 Thread 类
- 使用简单但过于繁重
class ThreadTestExtentsThread extends Thread{
public static void main(String[] args) {
ThreadTestExtentsThread testExtentsThread = new ThreadTestExtentsThread();
testExtentsThread.start();
}
}
- 实现Callable接口
class CallableThreadTest implements Callable<Integer>{
@Override
public Integer call() throws Exception {
Integer result = 123;
// do sth.
return result;
}
}
使用:
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableThreadTest threadTest = new CallableThreadTest();
// 指派一项要完成的任务,目标获得物件类型为Integer
FutureTask<Integer> task = new FutureTask<>(threadTest);
// 把task交托给thread
Thread thread007 = new Thread(task);
// 特工007开始执行任务:D (Target is locked)
thread007.start();
// 获得任务执行结果
Integer resultOfTask = task.get();
System.out.println(resultOfTask);
}
咳咳,使用007来理解FutureTask可能更清晰一些XD
唔,干脆发挥一下神盾局AOS……
// 众所周知神盾局致力于寻找o-8-4,随叫随到:D
class AOS implements Callable<O84>{
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 当然,这事儿运作得先有神盾局
AOS aos = new AOS();
// 还需要有任务
FutureTask<O84> mission = new FutureTask<>(aos);
// 任务需要指派特工去完成
Thread agent = new Thread(mission);
// 特工开始执行任务
agent.start();
/** 任务执行中 **/
// 获取行动结果-o84
O84 target = mission.get();
System.out.println(target);
}
@Override
public O84 call() throws Exception {
O84 target = new O84();
// 行动中
// Find out what is o84
// Like Thor's hammer XD
return target;
}
}
class O84 {
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
这样一想是不是清楚了很多咧:D (别趁现在回去看一次吼,时间瞬间……等等怎么一天过去了)
- 快速创建运行(jdk1.8+, lambda)
new Thread(() -> {
// do sth.
}).start();
实际是用runnable的:
new Thread(new Runnable() {
@Override
public void run() {
// do sth.
}
}).start();
线程通信
1. wait->notify
public class WaitNotifyTest {
private static Object goods = null;
private void waitNotify() throws InterruptedException {
new Thread(() -> {
synchronized (this){
while( goods == null ){
try{
System.out.println("1. [Consumer]Waiting");
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
System.out.println("3. [Consumer]Done");
}).start();
Thread.sleep(1000L);
new Thread(()->{
goods = new Object();
synchronized (this){
this.notifyAll();
System.out.println("2. [Consumer]NotifyAll");
}
}).start();
}
public static void main(String[] args) throws InterruptedException {
new WaitNotifyTest().waitNotify();
}
}
2. park->unpark
import java.util.concurrent.locks.LockSupport;
public class ParkUnparkTest {
private static Object goods = null;
public static void main(String[] args) throws InterruptedException {
new ParkUnparkTest().parkUnpark();
}
private void parkUnpark() throws InterruptedException {
Thread consumerThread = new Thread( ()->{
while( goods == null ){
System.out.println("1. [Consumer]Park");
LockSupport.park();
}
System.out.println("3. [Consumer]Done");
});
consumerThread.start();
Thread.sleep(1000L);
goods = new Object();
LockSupport.unpark(consumerThread);
System.out.println("2. [Consumer]Unpark");
}
}
3. resume->suspend (已弃用)
线程池
1. ThreadPoolExecutor
测试类
public class ThreadPoolTest {
public static void main(String[] args) throws Exception {
ThreadPoolTest threadPoolTest = new ThreadPoolTest();
threadPoolTest.threadPoolExecutorTest01();
threadPoolTest.threadPoolExecutorTest02(); // 分别执行请自行添加注释
threadPoolTest.threadPoolExecutorTest03();
}
private void submitMission(ThreadPoolExecutor threadPoolExecutor) throws Exception{
for (int i = 0; i < 15; i++) {
int n = i;
threadPoolExecutor.submit(() -> {
// 提交15项任务,每项需执行3秒
try{
System.out.println("Start: "+n);
Thread.sleep(3000L);
System.out.println("Finish: "+n);
}catch (InterruptedException e){
e.printStackTrace();
}
});
System.out.println("Submitted: "+i);
}
// 在提交完任务到任务执行完成之间,输出线程池状态
Thread.sleep(500L);
System.out.println("PoolSize: "+threadPoolExecutor.getPoolSize());
System.out.println("QueueSize: "+threadPoolExecutor.getQueue().size());
// 预期为5项任务全部执行完成之后,输出线程池状态
Thread.sleep(15000L);
System.out.println("PoolSize: "+threadPoolExecutor.getPoolSize());
System.out.println("QueueSize: "+threadPoolExecutor.getQueue().size());
}
}
测试01代码:
- 池子核心线程数:5
- 最大数量:10
- 超限策略:等待队列(不指定容量)
private void threadPoolExecutorTest01() throws Exception {
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
5, // keepAliveTime
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>());
submitMission(tpe);
}
测试01结果:
Submitted: 0
Start: 0
Submitted: 1
Submitted: 2
Start: 1
Submitted: 3
Start: 2
Submitted: 4 ← 5项任务提交
Start: 3
Start: 4 ← 5项任务启动
Submitted: 5
Submitted: 6
Submitted: 7
Submitted: 8
Submitted: 9
Submitted: 10
Submitted: 11
Submitted: 12
Submitted: 13
Submitted: 14 ← 所有任务提交
PoolSize: 5 ← 池子中线程数
QueueSize: 10 ← 等待队列中任务数量:10
Finish: 4
Finish: 1
Finish: 3
Finish: 0
Finish: 2 ← 5项任务结束
Start: 8
Start: 7
Start: 6
Start: 5
Start: 9 ← 后5项任务启动
Finish: 7
Finish: 8
Finish: 5
Finish: 6
Finish: 9 ← 后5项任务结束
Start: 13
Start: 12
Start: 11
Start: 10
Start: 14 ← 最后5项任务启动
Finish: 12
Finish: 13
Finish: 10
Finish: 14
Finish: 11 ← 最后5项任务结束
PoolSize: 5
QueueSize: 0 ← 等待队列为空
测试02代码:
- 池子核心线程数:5
- 最大数量:10
- 超限策略:容量为3的等待队列
- 异常条件:线程数量>池子中线程数最大数量+等待队列容量
- 异常策略:
private void threadPoolExecutorTest02() throws Exception {
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
5, // keepAliveTime
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) {
System.err.println("Mission rejected");
}
});
submitMission(tpe);
}
测试02结果:
Submitted: 0
Start: 0
Submitted: 1
Submitted: 2
Start: 1
Start: 2
Submitted: 3
Start: 3
Submitted: 4 ← 5项任务提交
Submitted: 5
Submitted: 6
Start: 4 ← 5项任务启动
Submitted: 7
Submitted: 8
Submitted: 9
Start: 8
Submitted: 10
Start: 9
Submitted: 11
Start: 10
Submitted: 12 ← 等待队列中任务数量:3
Start: 11
Mission rejected ← 此处提交数已经达到14>最大线程数10+等待队列容量3,拒绝任务
Submitted: 13
Mission rejected ← 同理拒绝任务
Start: 12
Submitted: 14
PoolSize: 10 ← 已经达到池子最大线程数,但是核心还是5
QueueSize: 3
Finish: 0
Finish: 4
Finish: 1
Finish: 3
Finish: 2 ← 核心数量5所以先执行5项任务
Start: 7 ← 开始取出等待队列中的任务
Start: 5
Start: 6 ← 取完等待队列中的任务
Finish: 10
Finish: 11
Finish: 9
Finish: 8
Finish: 12
Finish: 5
Finish: 7
Finish: 6 ← 执行结束,不包含任务14和15,他们被rejected了
以上两则测试演示了ThreadPoolExecutor中池子最大线程数和等待队列的效果,注意点:
- 在设置了等待队列但没有指定队列容量时,线程池会一直只用核心线程数(即 最大线程数配置项maximumPoolSize无效)
- 在给定了等待队列容量后,线程池在开到最大线程数后才向等待队列里放任务,最终提交任务的数量超过最大线程数+等待队列容量时,任务会使用RejectedExecutionHandler回绝
- 方案权衡:
- 不指定等待队列容量时,投入过多的任务会一直在队列里等待执行,如果出现外部中断则会丢失任务信息,在外力不可拒绝的情况下需提前保证任务数量不会太多
- 指定等待队列容量时,超出限额的任务会被拒绝,需安排拒绝策略
简图:
等待队列可视为右方的蓄水箱,不设定大小则相当于无限积水,左侧水箱maxSize永远不会满; 但是如果等待队列有容量,左侧水箱自然也就会出现溢出到maxSize的情况(即num-capacity-corePoolSize需≤maximumPoolSize)
2. ScheduledThreadPoolExecutor