- 创建文件
- vim
- mv
- cp
- 创建目录
- mkdir [option] dir-name
- 查看所有文件(包括隐藏文件)
ls -a - 查看目录树
- tree -a 显示所有
- tree -d 仅显示目录
- tree -L n n代表数字,表示要显示多少层(L必须大写)
- MD5
md3sum file - 删除文件
rm [option] file
如果没有-r选项,rm不会删除目录
-f 忽略不存在的文件,从不给出提示
-r 指示rm将参数中列出的全部目录和子目录均递归地删除
-i 进行交互式删除
Java并发编程实践-笔记
Java Concurrency in Practice
- 为什么要并发编程?
- 多核时代!
- 大部分程序只是碰巧是对的.
“Java 底层机制与设计层必要的策略之间存在着抽象的不匹配.为了解决这个问题,我们呈现了一个经过简化的规则集,用来编写并发程序.”
- 代码
介绍
- 基本概念:Socket, signal handlers, shared memory, semaphores
线程允许程序控制流(control flow)的多重分支同时存在于一个进程.它们共享进程范围内的资源,比如内存和文件句柄,但是每个线程都有自己的程序计数器(program counter),栈(stack)和本地变量. - NPTL线程包,作为Linux发布的一部分,设计它的目的是用来支持数十万甚至更多的线程.
- AWT和Swing工具集用事件派发线程(event dispatch thread,EDT)取代了主事件循环.当一个用户界面事件发生时,比如按下一个按钮,事件线程调用程序定义的事件处理器.
- 安全风险
- 多线程中各个操作的顺序是不可预测的
- 竞争条件(race condition)
- 活跃度危险
- 活跃度失败(liveness failure): 一个活动进入某种它永远无法再继续执行的状态.
- 死锁(deadlock),饥饿(starvation),活锁(livelock)
第一部分 线程安全(Thread Safety)
编写线程安全的代码,本质上就是管理对 状态 的访问,而且通常都是 共享的,可变的 状态.
我们讨论的线程安全性好像是关于代码的,但是真正要做的是在不可控制的并发访问中保护数据.
synchronized:独占锁
线程安全性:一个类是线程安全的,是指在被多个线程访问时,类可以持续进行正确的行为.
无状态对象永远是线程安全的.
- 竞争条件
当计算的正确性依赖于运行时相关的时序或者多线程的交替时,会产生竞争条件.
竞争条件的典型例子:- 计数器递增(读-改-写)
- 惰性初始化(检查再运行)
竞争条件和数据竞争
原子操作
synchronized块的两部分:锁对象的引用,以及这个锁保护的代码块
每个java对象都可以隐式地扮演一个用于同步的锁的角色;这些内置的锁被称作内部锁(intrinsic locks)或监控器锁(monitor locks).执行线程进入synchronized块之前会自动获得锁;而无论通过正常控制路径退出,还是从块中抛出异常,线程都在放弃对synchronized块的控制时自动释放锁.获得内部锁的唯一途径是:进入这个内部锁保护的同步块或方法.
内部锁在java中扮演了互斥锁(mutual exclusion lock,也称作mutex)的角色,意味着至多只有一个线程可以拥有锁.
- 重进入(Reentrancy)
当一个线程请求其他线程已经占有的锁时,请求线程将被阻塞.然而内部锁是可重进入的.因此,线程在试图获得它自己占有的锁时,请求会成功.
重进入意味着锁的请求是基于 每线程 的,而不是基于 每调用 的.
重进入的实现是通过每个锁关联一个请求计数(acquisition count)和一个占有它的线程.当计数为0时,认为锁是未被占有的.线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数置为1.如果同一线程再次请求这个锁,计数将递增;每次占用线程退出同步块,计数值将递减.直到计数器到0时,锁被释放.
如果内部锁不是可重入的,下面代码将产生死锁.
1 | public class Widget { |
- 锁保护: 对于每个可被多个线程访问的可变状态变量,如果所有访问它的线程在执行时都占有 同一个锁 ,这种情况下,我们称这个变量是由这个锁保护的.
Vector是线程安全的
对于每一个涉及多个变量的不变约束,需要同一个锁保护其所有的变量.
弱并发(poor concurrency)
共享对象(Sharing Ojects)
- 重排序
重排序通常是编译时或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段.重排序分为两类: 编译期重排序 和 运行期重排序 ,分别对应编译时和运行时环境.
在并发程序中,程序员会特别关注不同进程或线程之间的数据同步,特别是多个线程修改同一变量时,必须采取可靠的同步或其他措施保障数据被正确的修改.一条重要的原则是:不要假设指令执行的顺序,你无法预知不同线程之间的指令会以何种顺序执行.
在单线程程序中,通常假设指令是 顺序执行 的.
编译期重排序:编译期重排序的典型就是通过调整指令顺序,在不改变程序语义的前提下,尽可能减少寄存器的读取,存储次数,充分复用寄存器的存储值.
内存可见性
当读两个线程同时访问同一个变量时,synchronized用于确保写线程更新变量后,读线程再访问该变量时可以读取到该变量最新的值.
java存储模型中的重排序
在java的存储模型(Java Memory Model,JMM)中,重排序是十分重要的一节,特别是并发编程中.JMM通过 happens-before 法则保证顺序执行语义,如果想要让执行操作B的线程观察到执行操作A的线程的结果,那么A和B就必须满足happens-before原则,否则,JVM就会对它们进行任意排序以提高程序性能.
volatile 关键字可以 保证变量的可见性 ,因为对volitile的操作都在MainMemory中,而Main Memory是被所有线程所共享的,这里的代价是牺牲了性能,无法利用寄存器或Cache,因为它们都不是全局的,无法保证 可见性 ,可能产生脏读.
volatile还有一个作用就是 局部阻止重排序的发生 ,对volatile变量的操作指令都不会被重排序,因为重排序,又可能产生可见性问题.
如果一个字段声明为volatile型,线程对这个字段写入后,在执行后续的内存访问之前,线程必须刷新这个字段,且让这个字段对其他线程可见(即该字段立即刷新).每次对volatile字段的读访问,都要重新装载字段的值.
在保证可见性方面,锁(包括显示锁,对象锁)以及原子变量的读写都可以保证变量的可见性.
当一个线程在没有同步的情况下读取变量,它可能会得到一个过期值.但是至少它可以看到某个线程在那里设定的一个真实数值,而不是一个凭空而来的值.这样的安全保证被称为 最低限的安全性(out-of-thin-air safety)
最低限的安全应用与所有变量,除了一个例外:没有声明为volatile的64位数值变量(double和long).Java存储模型要求获取和存储操作都为原子的,但是对于非volatile的long和double变量,
JVM允许将64位的读或写划分为两个32位的操作.如果读和写发生在不同的线程,这种情况读取一个非volatile类型long就可能出现得到一个高32位和另一个值的低32位.
锁不仅仅是关于同步与互斥 的,也是关于内存可见的.为了保证所有线程都能看到共享的,可变变量的最新值,读取和写入线程必须使用公共的锁进行同步.
volatile变量的操作不会加锁,也就不会引起执行线程的阻塞,这使得volatile变量相对于synchronized而言,只是轻量级的同步机制.
volatile的语义不足以使自增操作(count++)原子化,除非你能保证只有一个线程对变量执行写操作.原子变量提供了”读-改-写”原子操作的支持,而且常被用作”更优的volatile变量”,加锁可以保证可见性与原子性,volatile变量只能保证保证可见性. - 调试提示
对于服务器应用程序,确保无论是在开发阶段还是测试阶段,启动JVM时都使用-server命令行选项,server模式的JVM会比client模式的JVM执行更多的优化,比如把没有在循环体中修改的变量提升到循环体外部;在开发环境(client模式的JVM)中可以工作的代码,可能会在部署环境(server环境的JVM)中失败. 发布和逸出
发布(publishing)一个对象的意思是使它能够被当前范围之外的代码所使用.
逸出是指一个对象在尚未准备好时就将它发布.
使用工厂方法防止this引用再构造期间逸出1
2
3
4
5
6
7
8
9
10
11
12
13
14public class SafeListener{
private final EventListener listener;
private SafeListener(){
listener = new EventListener(){
public void onEvent(Event e){
doSomething(e);
};
}
public static SafeListener newInstance(Event e){
SafeListener safe = new SafeListener();
source.registerListener(safe.listener);
return safe;
}
}Ad-hoc 线程限制
Ad-hoc线程限制是指维护线程限制性的任务全部落在实现上的这种情况.因为没有可见性修饰符与本地变量等语言特性协助将对象限制在目标线程上,所以这种方式是非常容易出错的.(Ad-hoc的言外之意是”非正式的”)- 栈限制
栈限制是线程限制的一种特例,在栈限制中,只能通过本地变量才能触及对象.正如封装使不变约束更容易被保持,本地变量使对象更容易被限制在线程本地中.本地变量本身就被限制再执行线程中,它们存在于执行线程栈.其他线程无法访问这个栈. ThreadLocal
一种维护贤臣限制的更加规范的方式是使用ThreadLocal,它允许你将每个线程与持有数值的对象关联在一起.ThreadLocal提供了get与set访问器,为每个使用它的线程维护一份单独的拷贝.所以get总是返回由当前执行线程通过set设置的最新值.
线程本地(ThreadLocal)变量通常用于防止在基于可变的单体(Singleton)或全局变量的设计中,出现不正确的共享.
概念上可以将ThreadLocal看做map<Thread,T>,它存储了与线程相关的值,不过事实上它并非这样实现的.与线程有关的值存储在线程对象自身中,线程终止后,这些值会被垃圾回收. 不理解对象的不变约束和后验条件,你就不能保证线程安全性.要约束状态变量的有效值或者状态转换,就需要原子性与封装性.
私有锁保护状态
1
2
3
4
5
6
7
8
9
10public class PrivateLock {
private final Object myLock = new Object();
Widget widget;
void someMethod() {
synchronized(myLock) {
// 访问或修改Widget的状态
}
}
}
使用私有锁对象,而不是对象内部锁(或其他可公共访问的锁)的好处:
私有的锁对象可以封装锁,这样客户代码无法得到它.而可公共访问的锁允许客户代码涉足它的同步策略–正确或不正确地.客户不正确地得到另一个对象的锁,会引起活跃度方面的问题.另外要验证程序是否正确地使用着一个可公共访问的锁,需要检查完整的程序,而不是一个单独的类.
(Liveness,A concurrent application’s ability to excute in a timely manner is known as its liveness.deadlock,starvation and livelock are the most common kind of liveness problem.)
关于volatile变量的一条规则:当且仅当一个变量没有参与那些涉及其他状态变量的不变约束时,才适合声明为volatile类型.
- 客户端加锁
1
2
3
4
5
6
7
8
9
10
11
12 // 非线程安全的"缺少即加入"实现
// !!!问题:这样的设计,外部能获得list的引用吗?如果不能,其他线程能同时修改list内容吗?
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
...
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent)
list.add(x);
return absent;
}
}
这里的问题在于同步行为发生在错误的锁上.即使一个list的操作全部声明为synchronized,但是使用了不同锁,将意味着putIfAbsent对于List的其他操作而言,并不是原子化的.所以当putIfAbsent执行时,不能保证另一个线程不会修改list.1
2
3
4
5
6
7
8
9
10
11
12// 使用客户端加锁的"缺少即加入"实现
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
...
public boolean putIfAbsent(E x) {
synchronized(list){
boolean absent = !list.contains(x);
if(absent)
list.add(x);
return absent;
}
}
1 | // 使用组合(composition)实现"缺少即加入" |
构建块(Building Blocks)
ConcurrentMap 接口
1
2
3
4
5
6
7
8
9
10public interface ConcurrentMap<K, V> extends Map<K, V> {
V putIfAbsent(K key, V value);
boolean remove(Object key, Object value);
boolean replace(K key, V oldValue, V newValue);
V replace(K key, V value);
}CopyOnWriteArrayList
“写入时复制(copy-on-write)”容器的迭代保留一个底层基础数组(the backing array)的引用.这个数组作为迭代器的起点,永远不会被修改,因此多个线程可以对这个容器进行迭代,并且不会受到另一个或多个想要修改容器的线程带来的干涉.”写入时复制”容器返回的迭代器不会抛出ConcurrentModificationException,并且返回的元素严格与迭代器创建时相一致,不会考虑后续的修改.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
/**
* Sets the array.
*/
final void setArray(Object[] a) {
array = a;
}
/**
* Returns <tt>true</tt> if this list contains all of the elements of the
* specified collection.
*
* @param c collection to be checked for containment in this list
* @return <tt>true</tt> if this list contains all of the elements of the
* specified collection
* @throws NullPointerException if the specified collection is null
* @see #contains(Object)
*/
public boolean containsAll(Collection<?> c) {
Object[] elements = getArray();
int len = elements.length;
for (Object e : c) {
if (indexOf(e, elements, 0, len) < 0)
return false;
}
return true;
}生产者-消费者模式
生产者和消费者可以并发地执行,如果一个受限于I/O,另一个受限于CPU,那么并发执行的全部产出会高于顺序执行的产出(想起我写过的loader).- 双端队列和窃取工作
正如阻塞队列适用于生产者-消费者模式一样,双端队列使它们自身与一种叫做窃取工作(work stealing)的模式相关联.在生产者-消费者模式中,所有消费者只共享一个工作队列;在窃取工作的设计中,每一个消费者都有一个自己的双端队列.如果一个消费者完成了自己双端队列中的全部工作,它可以偷取其他消费者的双端队列中的末尾任务.因为工作者线程并不会竞争一个共享的任务队列,所以窃取工作模式比传统的生产者-消费者设计有更好的可伸缩性;大多数时候它们访问自己的双端队列,减少竞争.当一个工作者必须要访问另一个队列时,它会从尾部截取,而不是从头部,从而进一步降低对双端队列的竞争. - 闭锁
闭锁(latch)是一种Synchronizer,它可以延迟线程的进度直到线程到达终止(terminal)状态.一个闭锁工作起来就像一道大门: 直到闭锁达到终点状态之前,门一直是关闭的,没有线程能够通过,在终点状态到来的时候,门开了,允许所有线程通过.一旦闭锁到达了终点状态,它就不能再改变状态了,所以它会永远保持敞开状态. - FutureTask
FutureTask同样可以作为闭锁.(FutureTask的实现描述了一个抽象的可携带结果的计算).
FutureTask的计算是通过Callable实现的,它等价于一份可携带结果的Runnable,并且有3个状态:等待,运行和完成.1
2public class FutureTask<V> implements RunnableFuture<V>;
public interface RunnableFuture<V> extends Runnable, Future<V>;
状态1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/**
* The run state of this task, initially NEW. The run state
* transitions to a terminal state only in methods set,
* setException, and cancel. During completion, state may take on
* transient values of COMPLETING (while outcome is being set) or
* INTERRUPTING (only while interrupting the runner to satisfy a
* cancel(true)). Transitions from these intermediate to final
* states use cheaper ordered/lazy writes because values are unique
* and cannot be further modified.
*
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
FutureTask.get依赖与任务的状态.如果它已经完成,get可以立刻得到返回结果,否则会被阻塞直到任务转入完成状态,然后完后结果或者抛出异常.1
2
3
4
5
6public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
- 信号量(semaphore)
计数信号量(Counting semaphore)用来控制能够同时访问某特定资源的活动的数量或者同时执行某一给定操作的数量.计数信号量可以用来实现资源池或者给一个容器限定边界.
关卡(barrier)和闭锁
关卡与闭锁关键的不同在于,所有线程必须同时达到关卡点,才能继续处理.闭锁等待的是事件;关卡等待是其他线程.
Exchanger是关卡的另一种形式,它是一种两步关卡,在关卡点会交换数据.当两方进行的活动不对称时,Exchanger是非常有用的,比如当一个线程向缓冲写入一个数据,这时另一个线程充当消费者使用这个数据;这些线程可以使用Exchanger进行会面,并用完整的缓冲与空缓冲进行交换.当两个线程通过Exchanger交换对象时,交换为双方的对象建立了一个安全的发布.
在计算问题中,如果不涉及I/O或者访问共享数据,Ncpu或者Ncpu+1个线程产生最优吞吐量;更多的线程也不会有任何帮助,并可能在某种程度上降低性能,因为线程会竞争CPU和内存等资源.
Java源代码阅读-ArrayBlockingQueue
看了Java HashMap 源码解析感觉受益匪浅,体会到了对Java源码的理解可以到如此深入的程度.希望以后自己也能写出这样的好文章.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365/**
* A bounded {@linkplain BlockingQueue blocking queue} backed by an
* array. This queue orders elements FIFO (first-in-first-out). The
* <em>head</em> of the queue is that element that has been on the
* queue the longest time. The <em>tail</em> of the queue is that
* element that has been on the queue the shortest time. New elements
* are inserted at the tail of the queue, and the queue retrieval
* operations obtain elements at the head of the queue.
*
* <p>This is a classic "bounded buffer", in which a
* fixed-sized array holds elements inserted by producers and
* extracted by consumers. Once created, the capacity cannot be
* changed. Attempts to {@code put} an element into a full queue
* will result in the operation blocking; attempts to {@code take} an
* element from an empty queue will similarly block.
*
* <p>This class supports an optional fairness policy for ordering
* waiting producer and consumer threads. By default, this ordering
* is not guaranteed. However, a queue constructed with fairness set
* to {@code true} grants threads access in FIFO order. Fairness
* generally decreases throughput but reduces variability and avoids
* starvation.
*
* <p>This class and its iterator implement all of the
* <em>optional</em> methods of the {@link Collection} and {@link
* Iterator} interfaces.
*
* <p>This class is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
* @since 1.5
* @author Doug Lea
* @param <E> the type of elements held in this collection
*/
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
private static final long serialVersionUID = -817911632652898426L;
/** The queued items */
final Object[] items;
/** items index for next take, poll, peek or remove */
int takeIndex;
/** items index for next put, offer, or add */
int putIndex;
/** Number of elements in the queue */
int count;
/*
* Concurrency control uses the classic two-condition algorithm
* found in any textbook.
*/
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
/**
* Shared state for currently active iterators, or null if there
* are known not to be any. Allows queue operations to update
* iterator state.
*/
transient Itrs itrs = null;
// Internal helper methods
/**
* Circularly decrement i.
*/
final int dec(int i) {
return ((i == 0) ? items.length : i) - 1;
}
/**
* Returns item at index i.
*/
@SuppressWarnings("unchecked")
final E itemAt(int i) {
return (E) items[i];
}
/**
* Throws NullPointerException if argument is null.
*
* @param v the element
*/
private static void checkNotNull(Object v) {
if (v == null)
throw new NullPointerException();
}
/**
* Inserts element at current put position, advances, and signals.
* Call only when holding lock.
*/
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
}
/**
* Extracts element at current take position, advances, and signals.
* Call only when holding lock.
*/
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return x;
}
/**
* Deletes item at array index removeIndex.
* Utility for remove(Object) and iterator.remove.
* Call only when holding lock.
*/
void removeAt(final int removeIndex) {
// assert lock.getHoldCount() == 1;
// assert items[removeIndex] != null;
// assert removeIndex >= 0 && removeIndex < items.length;
final Object[] items = this.items;
if (removeIndex == takeIndex) {
// removing front item; just advance
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
} else {
// an "interior" remove
// slide over all others up through putIndex.
final int putIndex = this.putIndex;
for (int i = removeIndex;;) {
int next = i + 1;
if (next == items.length)
next = 0;
if (next != putIndex) {
items[i] = items[next];
i = next;
} else {
items[i] = null;
this.putIndex = i;
break;
}
}
count--;
if (itrs != null)
itrs.removedAt(removeIndex);
}
notFull.signal();
}
/**
* Creates an {@code ArrayBlockingQueue} with the given (fixed)
* capacity and default access policy.
*
* @param capacity the capacity of this queue
* @throws IllegalArgumentException if {@code capacity < 1}
*/
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
/**
* Creates an {@code ArrayBlockingQueue} with the given (fixed)
* capacity and the specified access policy.
*
* @param capacity the capacity of this queue
* @param fair if {@code true} then queue accesses for threads blocked
* on insertion or removal, are processed in FIFO order;
* if {@code false} the access order is unspecified.
* @throws IllegalArgumentException if {@code capacity < 1}
*/
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
/**
* Creates an {@code ArrayBlockingQueue} with the given (fixed)
* capacity, the specified access policy and initially containing the
* elements of the given collection,
* added in traversal order of the collection's iterator.
*
* @param capacity the capacity of this queue
* @param fair if {@code true} then queue accesses for threads blocked
* on insertion or removal, are processed in FIFO order;
* if {@code false} the access order is unspecified.
* @param c the collection of elements to initially contain
* @throws IllegalArgumentException if {@code capacity} is less than
* {@code c.size()}, or less than 1.
* @throws NullPointerException if the specified collection or any
* of its elements are null
*/
public ArrayBlockingQueue(int capacity, boolean fair,
Collection<? extends E> c) {
this(capacity, fair);
final ReentrantLock lock = this.lock;
lock.lock(); // Lock only for visibility, not mutual exclusion
try {
int i = 0;
try {
for (E e : c) {
checkNotNull(e);
items[i++] = e;
}
} catch (ArrayIndexOutOfBoundsException ex) {
throw new IllegalArgumentException();
}
count = i;
putIndex = (i == capacity) ? 0 : i;
} finally {
lock.unlock();
}
}
/**
* Inserts the specified element at the tail of this queue if it is
* possible to do so immediately without exceeding the queue's capacity,
* returning {@code true} upon success and throwing an
* {@code IllegalStateException} if this queue is full.
*
* @param e the element to add
* @return {@code true} (as specified by {@link Collection#add})
* @throws IllegalStateException if this queue is full
* @throws NullPointerException if the specified element is null
*/
public boolean add(E e) {
return super.add(e);
}
/**
* Inserts the specified element at the tail of this queue if it is
* possible to do so immediately without exceeding the queue's capacity,
* returning {@code true} upon success and {@code false} if this queue
* is full. This method is generally preferable to method {@link #add},
* which can fail to insert an element only by throwing an exception.
*
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
/**
* Inserts the specified element at the tail of this queue, waiting
* for space to become available if the queue is full.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
/**
* Inserts the specified element at the tail of this queue, waiting
* up to the specified wait time for space to become available if
* the queue is full.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
checkNotNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
}
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return dequeue();
} finally {
lock.unlock();
}
}
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return itemAt(takeIndex); // null when queue is empty
} finally {
lock.unlock();
}
}
// this doc comment is overridden to remove the reference to collections
// greater in size than Integer.MAX_VALUE
/**
* Returns the number of elements in this queue.
*
* @return the number of elements in this queue
*/
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
// this doc comment is a modified copy of the inherited doc comment,
// without the reference to unlimited queues.
/**
* Returns the number of additional elements that this queue can ideally
* (in the absence of memory or resource constraints) accept without
* blocking. This is always equal to the initial capacity of this queue
* less the current {@code size} of this queue.
*
* <p>Note that you <em>cannot</em> always tell if an attempt to insert
* an element will succeed by inspecting {@code remainingCapacity}
* because it may be the case that another thread is about to
* insert or remove an element.
*/
public int remainingCapacity() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return items.length - count;
} finally {
lock.unlock();
}
}
/**
* Removes a single instance of the specified element from this queue,
* if it is present. More formally, removes an element {@code e} such
* that {@code o.equals(e)}, if this queue contains one or more such
* elements.
* Returns {@code true} if this queue contained the specified element
* (or equivalently, if this queue changed as a result of the call).
*
* <p>Removal of interior elements in circular array based queues
* is an intrinsically slow and disruptive operation, so should
* be undertaken only in exceptional circumstances, ideally
* only when the queue is known not to be accessible by other
* threads.
*
* @param o element to be removed from this queue, if present
* @return {@code true} if this queue changed as a result of the call
*/
public boolean remove(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
if (o.equals(items[i])) {
removeAt(i);
return true;
}
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false;
} finally {
lock.unlock();
}
}
/**
* Returns {@code true} if this queue contains the specified element.
* More formally, returns {@code true} if and only if this queue contains
* at least one element {@code e} such that {@code o.equals(e)}.
*
* @param o object to be checked for containment in this queue
* @return {@code true} if this queue contains the specified element
*/
public boolean contains(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
if (o.equals(items[i]))
return true;
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false;
} finally {
lock.unlock();
}
}
/**
* Returns an array containing all of the elements in this queue, in
* proper sequence.
*
* <p>The returned array will be "safe" in that no references to it are
* maintained by this queue. (In other words, this method must allocate
* a new array). The caller is thus free to modify the returned array.
*
* <p>This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in this queue
*/
public Object[] toArray() {
Object[] a;
final ReentrantLock lock = this.lock;
lock.lock();
try {
final int count = this.count;
a = new Object[count];
int n = items.length - takeIndex;
if (count <= n)
System.arraycopy(items, takeIndex, a, 0, count);
else {
System.arraycopy(items, takeIndex, a, 0, n);
System.arraycopy(items, 0, a, n, count - n);
}
} finally {
lock.unlock();
}
return a;
}
/**
* Returns an array containing all of the elements in this queue, in
* proper sequence; the runtime type of the returned array is that of
* the specified array. If the queue fits in the specified array, it
* is returned therein. Otherwise, a new array is allocated with the
* runtime type of the specified array and the size of this queue.
*
* <p>If this queue fits in the specified array with room to spare
* (i.e., the array has more elements than this queue), the element in
* the array immediately following the end of the queue is set to
* {@code null}.
*
* <p>Like the {@link #toArray()} method, this method acts as bridge between
* array-based and collection-based APIs. Further, this method allows
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs.
*
* <p>Suppose {@code x} is a queue known to contain only strings.
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
* <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
*
* @param a the array into which the elements of the queue are to
* be stored, if it is big enough; otherwise, a new array of the
* same runtime type is allocated for this purpose
* @return an array containing all of the elements in this queue
* @throws ArrayStoreException if the runtime type of the specified array
* is not a supertype of the runtime type of every element in
* this queue
* @throws NullPointerException if the specified array is null
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
final int count = this.count;
final int len = a.length;
if (len < count)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), count);
int n = items.length - takeIndex;
if (count <= n)
System.arraycopy(items, takeIndex, a, 0, count);
else {
System.arraycopy(items, takeIndex, a, 0, n);
System.arraycopy(items, 0, a, n, count - n);
}
if (len > count)
a[count] = null;
} finally {
lock.unlock();
}
return a;
}
public String toString() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
int k = count;
if (k == 0)
return "[]";
final Object[] items = this.items;
StringBuilder sb = new StringBuilder();
sb.append('[');
for (int i = takeIndex; ; ) {
Object e = items[i];
sb.append(e == this ? "(this Collection)" : e);
if (--k == 0)
return sb.append(']').toString();
sb.append(',').append(' ');
if (++i == items.length)
i = 0;
}
} finally {
lock.unlock();
}
}
/**
* Atomically removes all of the elements from this queue.
* The queue will be empty after this call returns.
*/
public void clear() {
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int k = count;
if (k > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
items[i] = null;
if (++i == items.length)
i = 0;
} while (i != putIndex);
takeIndex = putIndex;
count = 0;
if (itrs != null)
itrs.queueIsEmpty();
for (; k > 0 && lock.hasWaiters(notFull); k--)
notFull.signal();
}
} finally {
lock.unlock();
}
}
/**
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
return drainTo(c, Integer.MAX_VALUE);
}
/**
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c, int maxElements) {
checkNotNull(c);
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int n = Math.min(maxElements, count);
int take = takeIndex;
int i = 0;
try {
while (i < n) {
@SuppressWarnings("unchecked")
E x = (E) items[take];
c.add(x);
items[take] = null;
if (++take == items.length)
take = 0;
i++;
}
return n;
} finally {
// Restore invariants even if c.add() threw
if (i > 0) {
count -= i;
takeIndex = take;
if (itrs != null) {
if (count == 0)
itrs.queueIsEmpty();
else if (i > take)
itrs.takeIndexWrapped();
}
for (; i > 0 && lock.hasWaiters(notFull); i--)
notFull.signal();
}
}
} finally {
lock.unlock();
}
}
/**
* Returns an iterator over the elements in this queue in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
* <p>The returned iterator is
* <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return an iterator over the elements in this queue in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}
/**
* Shared data between iterators and their queue, allowing queue
* modifications to update iterators when elements are removed.
*
* This adds a lot of complexity for the sake of correctly
* handling some uncommon operations, but the combination of
* circular-arrays and supporting interior removes (i.e., those
* not at head) would cause iterators to sometimes lose their
* places and/or (re)report elements they shouldn't. To avoid
* this, when a queue has one or more iterators, it keeps iterator
* state consistent by:
*
* (1) keeping track of the number of "cycles", that is, the
* number of times takeIndex has wrapped around to 0.
* (2) notifying all iterators via the callback removedAt whenever
* an interior element is removed (and thus other elements may
* be shifted).
*
* These suffice to eliminate iterator inconsistencies, but
* unfortunately add the secondary responsibility of maintaining
* the list of iterators. We track all active iterators in a
* simple linked list (accessed only when the queue's lock is
* held) of weak references to Itr. The list is cleaned up using
* 3 different mechanisms:
*
* (1) Whenever a new iterator is created, do some O(1) checking for
* stale list elements.
*
* (2) Whenever takeIndex wraps around to 0, check for iterators
* that have been unused for more than one wrap-around cycle.
*
* (3) Whenever the queue becomes empty, all iterators are notified
* and this entire data structure is discarded.
*
* So in addition to the removedAt callback that is necessary for
* correctness, iterators have the shutdown and takeIndexWrapped
* callbacks that help remove stale iterators from the list.
*
* Whenever a list element is examined, it is expunged if either
* the GC has determined that the iterator is discarded, or if the
* iterator reports that it is "detached" (does not need any
* further state updates). Overhead is maximal when takeIndex
* never advances, iterators are discarded before they are
* exhausted, and all removals are interior removes, in which case
* all stale iterators are discovered by the GC. But even in this
* case we don't increase the amortized complexity.
*
* Care must be taken to keep list sweeping methods from
* reentrantly invoking another such method, causing subtle
* corruption bugs.
*/
class Itrs {
/**
* Node in a linked list of weak iterator references.
*/
private class Node extends WeakReference<Itr> {
Node next;
Node(Itr iterator, Node next) {
super(iterator);
this.next = next;
}
}
/** Incremented whenever takeIndex wraps around to 0 */
int cycles = 0;
/** Linked list of weak iterator references */
private Node head;
/** Used to expunge stale iterators */
private Node sweeper = null;
private static final int SHORT_SWEEP_PROBES = 4;
private static final int LONG_SWEEP_PROBES = 16;
Itrs(Itr initial) {
register(initial);
}
/**
* Sweeps itrs, looking for and expunging stale iterators.
* If at least one was found, tries harder to find more.
* Called only from iterating thread.
*
* @param tryHarder whether to start in try-harder mode, because
* there is known to be at least one iterator to collect
*/
void doSomeSweeping(boolean tryHarder) {
// assert lock.getHoldCount() == 1;
// assert head != null;
int probes = tryHarder ? LONG_SWEEP_PROBES : SHORT_SWEEP_PROBES;
Node o, p;
final Node sweeper = this.sweeper;
boolean passedGo; // to limit search to one full sweep
if (sweeper == null) {
o = null;
p = head;
passedGo = true;
} else {
o = sweeper;
p = o.next;
passedGo = false;
}
for (; probes > 0; probes--) {
if (p == null) {
if (passedGo)
break;
o = null;
p = head;
passedGo = true;
}
final Itr it = p.get();
final Node next = p.next;
if (it == null || it.isDetached()) {
// found a discarded/exhausted iterator
probes = LONG_SWEEP_PROBES; // "try harder"
// unlink p
p.clear();
p.next = null;
if (o == null) {
head = next;
if (next == null) {
// We've run out of iterators to track; retire
itrs = null;
return;
}
}
else
o.next = next;
} else {
o = p;
}
p = next;
}
this.sweeper = (p == null) ? null : o;
}
/**
* Adds a new iterator to the linked list of tracked iterators.
*/
void register(Itr itr) {
// assert lock.getHoldCount() == 1;
head = new Node(itr, head);
}
/**
* Called whenever takeIndex wraps around to 0.
*
* Notifies all iterators, and expunges any that are now stale.
*/
void takeIndexWrapped() {
// assert lock.getHoldCount() == 1;
cycles++;
for (Node o = null, p = head; p != null;) {
final Itr it = p.get();
final Node next = p.next;
if (it == null || it.takeIndexWrapped()) {
// unlink p
// assert it == null || it.isDetached();
p.clear();
p.next = null;
if (o == null)
head = next;
else
o.next = next;
} else {
o = p;
}
p = next;
}
if (head == null) // no more iterators to track
itrs = null;
}
/**
* Called whenever an interior remove (not at takeIndex) occurred.
*
* Notifies all iterators, and expunges any that are now stale.
*/
void removedAt(int removedIndex) {
for (Node o = null, p = head; p != null;) {
final Itr it = p.get();
final Node next = p.next;
if (it == null || it.removedAt(removedIndex)) {
// unlink p
// assert it == null || it.isDetached();
p.clear();
p.next = null;
if (o == null)
head = next;
else
o.next = next;
} else {
o = p;
}
p = next;
}
if (head == null) // no more iterators to track
itrs = null;
}
/**
* Called whenever the queue becomes empty.
*
* Notifies all active iterators that the queue is empty,
* clears all weak refs, and unlinks the itrs datastructure.
*/
void queueIsEmpty() {
// assert lock.getHoldCount() == 1;
for (Node p = head; p != null; p = p.next) {
Itr it = p.get();
if (it != null) {
p.clear();
it.shutdown();
}
}
head = null;
itrs = null;
}
/**
* Called whenever an element has been dequeued (at takeIndex).
*/
void elementDequeued() {
// assert lock.getHoldCount() == 1;
if (count == 0)
queueIsEmpty();
else if (takeIndex == 0)
takeIndexWrapped();
}
}
/**
* Iterator for ArrayBlockingQueue.
*
* To maintain weak consistency with respect to puts and takes, we
* read ahead one slot, so as to not report hasNext true but then
* not have an element to return.
*
* We switch into "detached" mode (allowing prompt unlinking from
* itrs without help from the GC) when all indices are negative, or
* when hasNext returns false for the first time. This allows the
* iterator to track concurrent updates completely accurately,
* except for the corner case of the user calling Iterator.remove()
* after hasNext() returned false. Even in this case, we ensure
* that we don't remove the wrong element by keeping track of the
* expected element to remove, in lastItem. Yes, we may fail to
* remove lastItem from the queue if it moved due to an interleaved
* interior remove while in detached mode.
*/
private class Itr implements Iterator<E> {
/** Index to look for new nextItem; NONE at end */
private int cursor;
/** Element to be returned by next call to next(); null if none */
private E nextItem;
/** Index of nextItem; NONE if none, REMOVED if removed elsewhere */
private int nextIndex;
/** Last element returned; null if none or not detached. */
private E lastItem;
/** Index of lastItem, NONE if none, REMOVED if removed elsewhere */
private int lastRet;
/** Previous value of takeIndex, or DETACHED when detached */
private int prevTakeIndex;
/** Previous value of iters.cycles */
private int prevCycles;
/** Special index value indicating "not available" or "undefined" */
private static final int NONE = -1;
/**
* Special index value indicating "removed elsewhere", that is,
* removed by some operation other than a call to this.remove().
*/
private static final int REMOVED = -2;
/** Special value for prevTakeIndex indicating "detached mode" */
private static final int DETACHED = -3;
Itr() {
// assert lock.getHoldCount() == 0;
lastRet = NONE;
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
if (count == 0) {
// assert itrs == null;
cursor = NONE;
nextIndex = NONE;
prevTakeIndex = DETACHED;
} else {
final int takeIndex = ArrayBlockingQueue.this.takeIndex;
prevTakeIndex = takeIndex;
nextItem = itemAt(nextIndex = takeIndex);
cursor = incCursor(takeIndex);
if (itrs == null) {
itrs = new Itrs(this);
} else {
itrs.register(this); // in this order
itrs.doSomeSweeping(false);
}
prevCycles = itrs.cycles;
// assert takeIndex >= 0;
// assert prevTakeIndex == takeIndex;
// assert nextIndex >= 0;
// assert nextItem != null;
}
} finally {
lock.unlock();
}
}
boolean isDetached() {
// assert lock.getHoldCount() == 1;
return prevTakeIndex < 0;
}
private int incCursor(int index) {
// assert lock.getHoldCount() == 1;
if (++index == items.length)
index = 0;
if (index == putIndex)
index = NONE;
return index;
}
/**
* Returns true if index is invalidated by the given number of
* dequeues, starting from prevTakeIndex.
*/
private boolean invalidated(int index, int prevTakeIndex,
long dequeues, int length) {
if (index < 0)
return false;
int distance = index - prevTakeIndex;
if (distance < 0)
distance += length;
return dequeues > distance;
}
/**
* Adjusts indices to incorporate all dequeues since the last
* operation on this iterator. Call only from iterating thread.
*/
private void incorporateDequeues() {
// assert lock.getHoldCount() == 1;
// assert itrs != null;
// assert !isDetached();
// assert count > 0;
final int cycles = itrs.cycles;
final int takeIndex = ArrayBlockingQueue.this.takeIndex;
final int prevCycles = this.prevCycles;
final int prevTakeIndex = this.prevTakeIndex;
if (cycles != prevCycles || takeIndex != prevTakeIndex) {
final int len = items.length;
// how far takeIndex has advanced since the previous
// operation of this iterator
long dequeues = (cycles - prevCycles) * len
+ (takeIndex - prevTakeIndex);
// Check indices for invalidation
if (invalidated(lastRet, prevTakeIndex, dequeues, len))
lastRet = REMOVED;
if (invalidated(nextIndex, prevTakeIndex, dequeues, len))
nextIndex = REMOVED;
if (invalidated(cursor, prevTakeIndex, dequeues, len))
cursor = takeIndex;
if (cursor < 0 && nextIndex < 0 && lastRet < 0)
detach();
else {
this.prevCycles = cycles;
this.prevTakeIndex = takeIndex;
}
}
}
/**
* Called when itrs should stop tracking this iterator, either
* because there are no more indices to update (cursor < 0 &&
* nextIndex < 0 && lastRet < 0) or as a special exception, when
* lastRet >= 0, because hasNext() is about to return false for the
* first time. Call only from iterating thread.
*/
private void detach() {
// Switch to detached mode
// assert lock.getHoldCount() == 1;
// assert cursor == NONE;
// assert nextIndex < 0;
// assert lastRet < 0 || nextItem == null;
// assert lastRet < 0 ^ lastItem != null;
if (prevTakeIndex >= 0) {
// assert itrs != null;
prevTakeIndex = DETACHED;
// try to unlink from itrs (but not too hard)
itrs.doSomeSweeping(true);
}
}
/**
* For performance reasons, we would like not to acquire a lock in
* hasNext in the common case. To allow for this, we only access
* fields (i.e. nextItem) that are not modified by update operations
* triggered by queue modifications.
*/
public boolean hasNext() {
// assert lock.getHoldCount() == 0;
if (nextItem != null)
return true;
noNext();
return false;
}
private void noNext() {
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
// assert cursor == NONE;
// assert nextIndex == NONE;
if (!isDetached()) {
// assert lastRet >= 0;
incorporateDequeues(); // might update lastRet
if (lastRet >= 0) {
lastItem = itemAt(lastRet);
// assert lastItem != null;
detach();
}
}
// assert isDetached();
// assert lastRet < 0 ^ lastItem != null;
} finally {
lock.unlock();
}
}
public E next() {
// assert lock.getHoldCount() == 0;
final E x = nextItem;
if (x == null)
throw new NoSuchElementException();
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
if (!isDetached())
incorporateDequeues();
// assert nextIndex != NONE;
// assert lastItem == null;
lastRet = nextIndex;
final int cursor = this.cursor;
if (cursor >= 0) {
nextItem = itemAt(nextIndex = cursor);
// assert nextItem != null;
this.cursor = incCursor(cursor);
} else {
nextIndex = NONE;
nextItem = null;
}
} finally {
lock.unlock();
}
return x;
}
public void remove() {
// assert lock.getHoldCount() == 0;
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
if (!isDetached())
incorporateDequeues(); // might update lastRet or detach
final int lastRet = this.lastRet;
this.lastRet = NONE;
if (lastRet >= 0) {
if (!isDetached())
removeAt(lastRet);
else {
final E lastItem = this.lastItem;
// assert lastItem != null;
this.lastItem = null;
if (itemAt(lastRet) == lastItem)
removeAt(lastRet);
}
} else if (lastRet == NONE)
throw new IllegalStateException();
// else lastRet == REMOVED and the last returned element was
// previously asynchronously removed via an operation other
// than this.remove(), so nothing to do.
if (cursor < 0 && nextIndex < 0)
detach();
} finally {
lock.unlock();
// assert lastRet == NONE;
// assert lastItem == null;
}
}
/**
* Called to notify the iterator that the queue is empty, or that it
* has fallen hopelessly behind, so that it should abandon any
* further iteration, except possibly to return one more element
* from next(), as promised by returning true from hasNext().
*/
void shutdown() {
// assert lock.getHoldCount() == 1;
cursor = NONE;
if (nextIndex >= 0)
nextIndex = REMOVED;
if (lastRet >= 0) {
lastRet = REMOVED;
lastItem = null;
}
prevTakeIndex = DETACHED;
// Don't set nextItem to null because we must continue to be
// able to return it on next().
//
// Caller will unlink from itrs when convenient.
}
private int distance(int index, int prevTakeIndex, int length) {
int distance = index - prevTakeIndex;
if (distance < 0)
distance += length;
return distance;
}
/**
* Called whenever an interior remove (not at takeIndex) occurred.
*
* @return true if this iterator should be unlinked from itrs
*/
boolean removedAt(int removedIndex) {
// assert lock.getHoldCount() == 1;
if (isDetached())
return true;
final int cycles = itrs.cycles;
final int takeIndex = ArrayBlockingQueue.this.takeIndex;
final int prevCycles = this.prevCycles;
final int prevTakeIndex = this.prevTakeIndex;
final int len = items.length;
int cycleDiff = cycles - prevCycles;
if (removedIndex < takeIndex)
cycleDiff++;
final int removedDistance =
(cycleDiff * len) + (removedIndex - prevTakeIndex);
// assert removedDistance >= 0;
int cursor = this.cursor;
if (cursor >= 0) {
int x = distance(cursor, prevTakeIndex, len);
if (x == removedDistance) {
if (cursor == putIndex)
this.cursor = cursor = NONE;
}
else if (x > removedDistance) {
// assert cursor != prevTakeIndex;
this.cursor = cursor = dec(cursor);
}
}
int lastRet = this.lastRet;
if (lastRet >= 0) {
int x = distance(lastRet, prevTakeIndex, len);
if (x == removedDistance)
this.lastRet = lastRet = REMOVED;
else if (x > removedDistance)
this.lastRet = lastRet = dec(lastRet);
}
int nextIndex = this.nextIndex;
if (nextIndex >= 0) {
int x = distance(nextIndex, prevTakeIndex, len);
if (x == removedDistance)
this.nextIndex = nextIndex = REMOVED;
else if (x > removedDistance)
this.nextIndex = nextIndex = dec(nextIndex);
}
else if (cursor < 0 && nextIndex < 0 && lastRet < 0) {
this.prevTakeIndex = DETACHED;
return true;
}
return false;
}
/**
* Called whenever takeIndex wraps around to zero.
*
* @return true if this iterator should be unlinked from itrs
*/
boolean takeIndexWrapped() {
// assert lock.getHoldCount() == 1;
if (isDetached())
return true;
if (itrs.cycles - prevCycles > 1) {
// All the elements that existed at the time of the last
// operation are gone, so abandon further iteration.
shutdown();
return true;
}
return false;
}
// /** Uncomment for debugging. */
// public String toString() {
// return ("cursor=" + cursor + " " +
// "nextIndex=" + nextIndex + " " +
// "lastRet=" + lastRet + " " +
// "nextItem=" + nextItem + " " +
// "lastItem=" + lastItem + " " +
// "prevCycles=" + prevCycles + " " +
// "prevTakeIndex=" + prevTakeIndex + " " +
// "size()=" + size() + " " +
// "remainingCapacity()=" + remainingCapacity());
// }
}
/**
* Returns a {@link Spliterator} over the elements in this queue.
*
* <p>The returned spliterator is
* <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT},
* {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}.
*
* @implNote
* The {@code Spliterator} implements {@code trySplit} to permit limited
* parallelism.
*
* @return a {@code Spliterator} over the elements in this queue
* @since 1.8
*/
public Spliterator<E> spliterator() {
return Spliterators.spliterator
(this, Spliterator.ORDERED | Spliterator.NONNULL |
Spliterator.CONCURRENT);
}
}
面试复习
C++
- 虚函数
虚函数实现的一般模型: 每个类有一个虚表,内含该类之中有作用的虚函数的地址,然后每个对象有一个vptr, 指向虚表的所在.
每一个类有一个虚表,每一个类的对象有一个指向虚表的指针vptr
对于非虚函数,会转化为一个全局函数,调用方法时,对象作为第一个参数,隐式传给这个函数 传值与传参问题
++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14// 引自<<剑指Offer>>
class A{
private:
int value;
public:
A(int n){value = n; }
A(A other){value=other.value;}
void print(){ std::cout << value << std::endl;}
}
int _tmain(int argc, _TCHAR* argv[]){
A a = 10;
A b = a;
b.print();
}上述代码中,复制构造函数A(A other)传入的参数是A的一个实例.由于是传值参数,我们把形参复制到实参会调用复制构造函数.因此如果允许复制构造函数传值,就会在复制构造函数内调用复制构造函数,就会形成永无休止的递归调用从而导致栈溢出.
浅拷贝和深拷贝
在某些情况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,对象里的值完全复制给另一个对象,如A=B.这时如果B中有一个成员变量指针已经申请了内存,那么A中的那个成员变量也指向同一块内存.当B把内存释放了,这时A内的指针就是野指针了,会出现运行错误.
Java
Spring Framework
很好的面试资料: 69道Spring面试题和答案面向对象编程(OOP)
优点:
代码开发模块化,更易维护和修改
代码复用
增强代码的可靠性,灵活性,可读性
面向对象编程特性:- 封装,通过隐藏对象的属性来保护内部的状态;提高代码的可用性和可维护性;禁止对象之间不良交互提高模块化
- 继承,代码重用,添加新特性
- 多态,程序中定义的应用变量所指向的具体类型和通过引用变量调用的方法在编译时并不确定,而在程序运行时才能确定.
- 抽象,把想法从具体的实例抽离出来的步骤.
封装与抽象的不同:
抽象关注对象的行为,封装关注对象行为的细节.
Java虚拟机(JVM)
Java虚拟机是可以执行Java字节码的虚拟机进程,使跨平台变为可能.自动拆装箱
自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化,比如int->Integer.反之就是自定拆箱.死锁
两个进程都在等待对方执行完毕才能继续执行的时候就发生了死锁.结果两个进程都陷入了无限的等待中.活锁
活锁指的是任务或者执行者没有被阻塞,由于某些条件没有被满足,导致一直重复尝试,失败,尝试,失败.死锁活锁的区别
处于活锁的实体是在不断的改变状态,所谓的”活”,而处于死锁的实体表现为等待;活锁有可能解开,死锁则不能.如何确保N个线程可以访问N个资源同时又不导致死锁?
同样的顺序加锁和释放锁 : 指定获取锁的顺序,并强制线程按照指定的顺序获得锁.快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
Iterator的安全失败是基于对底层集合做拷贝,因此它不受原集合上修改的影响.jva.util包下的所有集合类都是快速失败的,而java.util.concurrent包下的所有类都是安全失败的.
快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器不会抛出这样的异常.
推荐使用CopyOnWriteArrayList. CopyOnWriteArrayList是ArrayList的一个线程安全的变体,其中所有可变操作(add,set等)都是通过对底层数组进行一次新的复制来实现的.
适用场景: 不想进行同步遍历,但又需要从并发线程中排除冲突;遍历操作远大于可变操作.try,catch,finnally,return
finally 块的语句在try或catch中的return语句 执行之后返回之前 执行且finally里的修改语句可能影响也可能不影响try或catch中return已确定的返回值,若finally里也有return语句则覆盖try或catch中的return语句直接返回.
参考Java finally语句到底是在return之前还是之后执行?串行(serial)收集器和吞吐量收集器的区别是什么?
吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序.而串行收集器对于大多数的小应用就足够了.多线程
Top 15 Java Multithreading, Concurrency Interview Questions Answers asked in Investment banks
1) 现在有T1,T2,T3三个线程,怎样保证T2在T1执行后执行,T3在T2执行后执行
使用join方法实现
2) 在java中lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户度,但只允许一个用户写,一次来保持它的完整性,你会怎么去实现它?
synchronized和lock的用法区别: 在需要同步的对象加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示要锁的对象.
lock:需要显式指定起始位置和终止位置. 一般使用ReentrantLock类作为锁,多个线程中必须使用一个ReentrantLock类作为对象才能保证锁的生效,且在加锁和解锁处需要通过lock()和unlock()显式指出.所以一般在finally块中谢unlock()以防死锁.
synchronized和lock的原理:
synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁.独占锁意味着其他线程只能依靠阻塞来等待线程释放锁.而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低.
而lock采用的是乐观锁方式.所谓乐观锁就是每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止.乐观锁实现的机制就是CAS操作(Compare and Swap).研究ReentrantLock的源代码,会发现其中一种比较重要的获取锁的方法是compareAndSetState,其实就是调用CPU提供的特殊指令.
现代的CPU提供了指令,可以自动更新共享数据,而且能够检测其他线程的干扰,而compareAndSet就用这些代替了锁定.这个算法称作非阻塞算法,意思是一个线程的失败或者挂起不应该引起其他线程的失败或挂起的算法.
…
3) 在java中wait与sleep的不同
在等待是wait会释放锁,而sleep一直持有锁.
wait通常被用于线程间交互,sleep通常被用于暂停执行.
4) 用java实现阻塞队列
阻塞队列与普通队列的区别在于: 当队列为空时,从队列中取元素的操作将会被阻塞; 当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素.同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或多个元素,或者完全清空队列.
从5.0开始,JDK在java.util.concurrent包里提供了阻塞队列的官方实现.
数据结构
- 数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15int GetSize(int data[]){
return sizeof(data);
}
int _tmain(int argc, _TCHAR* argv[]){
int data1[] = {1,2,3,4,5};
int size1 = sizeof(data1);
int* data2 = data1;
int size2 = sizeof(data2);
int size3 = GetSize(data1);
printf("%d, %d, %d", size1, size2, size3);
}
// output: 20, 4, 4
在C/C++中,当数组作为函数的参数进行传递时,数组就自动退化为同类型的指针.
- 满二叉树
满二叉树(full binary tree): 除最后一层无任何子节点外,每一层的所有节点都有两个子节点的二叉树.额\节点数达到最大值.所有叶子节点必须在同一层上.
(满二叉树->完全二叉树) 完全二叉树
完全二叉树(complete binary tree): 除了最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边若干节点.完全二叉树是由满二叉树引出来的.对于深度为K的,有n个节点的二叉树,当且仅当其每一个节点都与深度为K的满二叉树中编号从1至n的节点一一对应使称为完全二叉树.最大堆
最大堆:跟节点是所有节点中的最大者,且是完全二叉树.
语言特性
- C++中struct和class的区别
- 访问权限: 如果没有标明成员函数或者成员变量的访问权限级别,在struct中默认是public,而在class中默认是private.
设计模式
singleton
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Singleton {
// 第一步:私有构造函数
private Singleton(){}
// 第二步:私有变量
private Singleton instance = null;
// 第三步:静态方法
public static getInstance(){
if(instance==null){
synchronized{
if(instance==null){
return new Singleton();
}
}
}
return instance;
}
}引用和指针
引用与指针的区别在于表现形式(引用初始化后不能再指向其他对象);
传值参数与引用参数的区别在于形参的声明.
外部排序
外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次性装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的.
其他
理解数据之间的依赖性.