Java 提供两类适用于并发应用的集合 :
1> 阻塞式集合 : 当集合已满或为空时,被调用的添加或移除方法不能立即执行,此时这个线程阻塞,一直到该方法可以被成功执行
2> 非阻塞式集合 : 如果方法不能被立即执行,则返回 null 或抛出异常,但是调用这个方法的线程不会被阻塞
ConcurrentLinkedDeque 类
非阻塞式线程安全列表,线程安全的双向链表
public int size() : 返回链表中的元素量,但可能数据不是真实的
public E getFirst()、public E getLast() : 返回元素中的第一个、最后一个数据,且数据不会移除,如果列表为空将抛出 NoSuchElementException
public E peek()、public E peekFirst()、public E peekLast() : 返回元素中的第一个、第一个、最后一个数据,没有数据时返回 null
public E poll()、public E pollFirst()、public E pollLast() : 返回并移除第一个、第一个、最后一个数据,没有数据时返回 null
public E remove()、public E removeFirst()、public E removeLast() : 返回并移除第一个、第一个、最后一个数据,如果列表为空将抛出 NoSuchElementException
public class
AddTask
implements
Runnable {
private
ConcurrentLinkedDeque
list
;
public
AddTask(ConcurrentLinkedDeque list) {
this
.
list
= list;
}
@Override
public void
run() {
String name = Thread.
currentThread
().getName();
for
(
int
i =
0
; i <
10000
; i++) {
list
.add(name +
": Element "
+ i);
}
}
}
public class
PollTask
implements
Runnable {
private
ConcurrentLinkedDeque
list
;
public
PollTask(ConcurrentLinkedDeque list) {
this
.
list
= list;
}
@Override
public void
run() {
for
(
int
i =
0
; i <
5000
; i++) {
list
.pollFirst();
list
.pollLast();
}
}
public static void
main(String[] args) {
ConcurrentLinkedDeque list =
new
ConcurrentLinkedDeque<>();
Thread[] threads =
new
Thread[
100
];
for
(
int
i =
0
; i < threads.
length
; i++) {
AddTask addTask =
new
AddTask(list);
threads[i] =
new
Thread(addTask);
threads[i].start();
}
System.
out
.printf(
"Main: %d AddTask threads have been launched
\n
"
, threads.
length
);
try
{
for
(
int
i =
0
; i < threads.
length
; i++) {
threads[i].join();
}
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.
out
.printf(
"Main: Size of the List: %d
\n
"
, list.size());
for
(
int
i =
0
; i < threads.
length
; i++) {
PollTask pollTask =
new
PollTask(list);
threads[i] =
new
Thread(pollTask);
threads[i].start();
}
System.
out
.printf(
"Main: %d PollTask threads have been launched
\n
"
, threads.
length
);
for
(
int
i =
0
; i < threads.
length
; i++) {
try
{
threads[i].join();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
System.
out
.printf(
"Main: Size of the list: %d
\n
"
, list.size());
}
}
LinkedBlockingDeque 类
阻塞式线程安全列表,队列已满或为空操作不会立即执行
public LinkedBlockingDeque(int capacity) : 创建固定容量的 LinkedBlockingDeque 对象
public void put(E e) throws InterruptedException、public void putFirst(E e) throws InterruptedException、public void putLast(E e) throws InterruptedException : 向队列中 结尾、开头、结尾 插入元素,如果队列已满,则阻塞线程等待直到列表中有可用空间
public boolean add(E e)、public void addFirst(E e)、public void addLast(E e) : 向队列中 结尾、开头、结尾 插入元素,如果队列已满则抛出 IllegalStateException
public E take() throws InterruptedException、public E takeFirst() throws InterruptedException、public E takeLast() throws InterruptedException : 从列表中移除并返回第一个、第一个、最后一个元素,如果列表为空则线程将会被阻塞等待一直到列表中有元素
public E poll()、public E pollFirst()、public E pollLast() : 从列表中移除并返回第一个、第一个、最后一个元素,如果列表为空则返回 null
public E getFirst()、public E getLast() : 从列表中返回但不移除第一个、最后一个元素,如果列表为空则抛出 NoSuchElementException
public E peek()、public E peekFirst()、public E peekLast() : 取出但不移除队列中第一个、第一个、最后一个元素,如果列表为空则返回 null
public class
Client
implements
Runnable {
private
LinkedBlockingDeque
requestList
;
public
Client(LinkedBlockingDeque requestList) {
this
.
requestList
= requestList;
}
@Override
public void
run() {
for
(
int
i =
0
; i <
3
; i++) {
for
(
int
j =
0
; j <
5
; j++) {
StringBuilder request =
new
StringBuilder();
request.append(i);
request.append(
":"
);
request.append(j);
try
{
requestList
.put(request.toString());
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.
out
.printf(
"Client: %s at %s.
\n
"
, request,
new
Date());
}
try
{
TimeUnit.
SECONDS
.sleep(
2
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
System.
out
.printf(
"Clients: End.
\n
"
);
}
public static void
main(String[] args)
throws
InterruptedException {
LinkedBlockingDeque list =
new
LinkedBlockingDeque<>(
3
);
Client client =
new
Client(list);
Thread thread =
new
Thread(client);
thread.start();
for
(
int
i =
0
; i <
5
; i++) {
for
(
int
j =
0
; j <
3
; j++) {
String request = list.take();
System.
out
.printf(
"Main: Request: %s at %s. Size: %d
\n
"
, request,
new
Date(), list.size());
}
TimeUnit.
MILLISECONDS
.sleep(
300
);
}
System.
out
.printf(
"Main: End of the program.
\n
"
);
}
}
PriorityBlockingQueue 类
阻塞式数据结构,所有添加的元素必须实现 Comparable 接口用于排序
public PriorityBlockingQueue() : 创建一个默认数量为 11 的队列,队列中的元素根据 Comparable 进行排序
public boolean add(E e) : 向队列中插入一个优先级队列数据
public void put(E e) : 向队列中插入一个优先级队列数据。因为队列是无界的,所以这个方法永远不会阻塞
public int size() : 返回队列中元素的个数,如果队列中元素个数超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE
public E poll() : 移除并返回队列中第一个元素,如果队列为空则返回 null
public E take() throws InterruptedException : 移除并返回队列中第一个元素,如果队列为空则阻塞线程直到有元素取出
public E peek() : 返回但不移除队列中的数据,如果队列中的数据为空则返回 null
public void clear() : 移除队列中所有元素
public class
Event
implements
Comparable {
private int
thread
;
private int
priority
;
public
Event(
int
thread,
int
priority) {
this
.
thread
= thread;
this
.
priority
= priority;
}
public int
getThread() {
return
thread
;
}
public int
getPriority() {
return
priority
;
}
@Override
public int
compareTo(Event event) {
if
(
this
.
priority
> event.getPriority()) {
return
-
1
;
}
else if
(
this
.
priority
< event.getPriority()) {
return
1
;
}
return
0
;
}
}
public class
Task
implements
Runnable {
private int
id
;
private
PriorityBlockingQueue
queue
;
public
Task(
int
id, PriorityBlockingQueue queue) {
this
.
id
= id;
this
.
queue
= queue;
}
@Override
public void
run() {
for
(
int
i =
0
; i <
1000
; i++) {
Event event =
new
Event(
id
, i);
queue
.add(event);
}
}
public static void
main(String[] args)
throws
InterruptedException {
PriorityBlockingQueue queue =
new
PriorityBlockingQueue<>();
Thread[] taskThreads =
new
Thread[
5
];
for
(
int
i =
0
; i < taskThreads.
length
; i++) {
Task task =
new
Task(i, queue);
taskThreads[i] =
new
Thread(task);
taskThreads[i].start();
}
for
(
int
i =
0
; i < taskThreads.
length
; i++) {
taskThreads[i].join();
}
System.
out
.printf(
"Main: Queue Size: %d
\n
"
, queue.size());
for
(
int
i =
0
; i < taskThreads.
length
*
1000
; i++) {
Event event = queue.poll();
System.
out
.printf(
"Thread %s: Priority %d
\n
"
, event.getThread(), event.getPriority());
}
System.
out
.printf(
"Main: Queue Size: %d
\n
"
, queue.size());
System.
out
.printf(
"Main: End of the program
\n
"
);
}
}
DelayQueue 类
用于存放带有激活日期的元素,当调用方法从队列中返回或提取元素时,未来的元素日期将被忽略
DelayQueue 类 中的元素必须继承 Delayed接口,Delayed接口 继承于 Comparable接口因此有 compareTo方法,这样存放的对象具有激活时间,该接口两个方法 :
1> int compareTo(T o) : 如果当前对象延迟值小于参数对象值,将返回一个小于 0 的值;延迟值大于参数对象,返回一个大于0的值;如果两者延迟值相等则返回0
2> long getDelay(TimeUnit unit) : 返回激活日期剩余时间单位
public E poll() : 返回并移除队列头数据,或者队列中没有一个过期延迟对象返回 null
public E take() throws InterruptedException : 返回并移除第一个元素,如果延迟队列中没有元素则一直等待
public int size() : 返回队列中元素的个数,如果队列中元素个数超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE。该方法返回元素的总个数,包括活动和非活动元素
public void clear() : 移除队列中所有元素
public boolean offer(E e) : 插入特定元素到延迟队列中
public E peek() : 返回但不移除第一个元素,如果在过期队列中没有元素将返回即将过期的数据,如果依旧没有则返回 null
public class
Event
implements
Delayed {
private
Date
startDate
;
public
Event(Date startDate) {
this
.
startDate
= startDate;
}
@Override
public long
getDelay(TimeUnit unit) {
Date now =
new
Date();
long
diff =
startDate
.getTime() - now.getTime();
return
unit.convert(diff, TimeUnit.
MILLISECONDS
);
}
@Override
public int
compareTo(Delayed o) {
long
result =
this
.getDelay(TimeUnit.
NANOSECONDS
) - o.getDelay(TimeUnit.
NANOSECONDS
);
if
(result <
0
) {
// 当前对象的延迟值小于参数对象值
return
-
1
;
}
else if
(result >
0
) {
// 当前对象的延迟值大于参数对象值
return
1
;
}
return
0
;
// 两者的延迟值相等
}
}
public class
Task
implements
Runnable {
private int
id
;
private
DelayQueue
queue
;
public
Task(
int
id, DelayQueue queue) {
this
.
id
= id;
this
.
queue
= queue;
}
@Override
public void
run() {
Date now =
new
Date();
Date delay =
new
Date();
delay.setTime(now.getTime() + (
id
*
1000
));
System.
out
.printf(
"Thread %s: %s
\n
"
,
id
, delay);
for
(
int
i =
0
; i <
100
; i++) {
Event event =
new
Event(delay);
queue
.add(event);
}
}
public static void
main(String[] args)
throws
InterruptedException {
DelayQueue queue =
new
DelayQueue<>();
Thread[] threads =
new
Thread[
5
];
for
(
int
i =
0
; i < threads.
length
; i++) {
Task task =
new
Task(i +
1
, queue);
threads[i] =
new
Thread(task);
threads[i].start();
}
for
(
int
i =
0
; i < threads.
length
; i++) {
threads[i].join();
}
do
{
int
counter =
0
;
Event event;
do
{
event = queue.poll();
if
(event !=
null
) {
counter++;
}
}
while
(event !=
null
);
System.
out
.printf(
"At %s you have read %d events
\n
"
,
new
Date(), counter);
}
while
(queue.size() >
0
);
}
}
线程安全可遍历
ConcurrentNavigableMap接口
该接口 public interface ConcurrentNavigableMap extends ConcurrentMap, NavigableMap 有两个存放元素,一个唯一标示元素另一个是其它部分数据
ConcurrentSkipListMap类
ConcurrentSkipListMap类 实现 ConcurrentNavigableMap接口,public class ConcurrentSkipListMap extends AbstractMap implements ConcurrentNavigableMap, Cloneable, Serializable
ConcurrentSkipListMap类 内部实现是通过一个 Skip List存放数据,Skip List基于并发列表的数据结构
ConcurrentSkipListMap类 插入的元素会使用键值来排序所有元素
public V put(K key, V value) : 在 map 中使用特定的 key 来关联特定的 value,如果 map 之前已经包含这个 key值,则旧值将会被取代
public Map.Entry firstEntry() : 返回map中第一个元素,如果map为空返回 null,返回实体不支持 Entry.setValue 方法
public class
Contact {
private
String
name
;
private
String
phone
;
public
Contact(String name, String phone) {
this
.
name
= name;
this
.
phone
= phone;
}
public
String getName() {
return
name
;
}
public
String getPhone() {
return
phone
;
}
}
public class
Task
implements
Runnable {
private
ConcurrentSkipListMap
map
;
private
String
id
;
public
Task(ConcurrentSkipListMap map, String id) {
this
.
map
= map;
this
.
id
= id;
}
@Override
public void
run() {
for
(
int
i =
0
; i <
1000
; i++) {
Contact contact =
new
Contact(
id
, String.
valueOf
(i +
1000
));
map
.put(
id
+ contact.getPhone(), contact);
}
}
public static void
main(String[] args) {
ConcurrentSkipListMap map =
new
ConcurrentSkipListMap<>();
Thread[] threads =
new
Thread[
25
];
int
counter =
0
;
for
(
char
i =
'A'
; i <
'Z'
; i++) {
Task task =
new
Task(map, String.
valueOf
(i));
threads[counter] =
new
Thread(task);
threads[counter].start();
counter++;
}
try
{
for
(
int
i =
0
; i <
25
; i++) {
threads[i].join();
}
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.
out
.printf(
"Main: Size of the map: %d
\n
"
, map.size());
Map.Entry element = map.firstEntry();
Contact contact = element.getValue();
System.
out
.printf(
"Main: First Entry: %s: %s
\n
"
, contact.getName(), contact.getPhone());
element = map.lastEntry();
contact = element.getValue();
System.
out
.printf(
"Main: Last Entry: %s: %s
\n
"
, contact.getName(), contact.getPhone());
System.
out
.printf(
"Main: Submap from A1996 to B1002:
\n
"
);
ConcurrentNavigableMap submap = map.subMap(
"A1996"
,
"B1002"
);
do
{
element = submap.pollFirstEntry();
if
(element !=
null
) {
contact = element.getValue();
System.
out
.printf(
"%s: %s
\n
"
, contact.getName(), contact.getPhone());
}
}
while
(element !=
null
);
}
}
ThreadLocalRandom 类
ThreadLocalRandom 类线程本地变量,相比使用共享 Random对象其性能更好
public static ThreadLocalRandom current() : 返回当前线程的 ThreadLocalRandom 对象
public int nextInt(int bound) : 返回介于指定范围的数据,类似方法还有其它
public class
TaskLocalRandom
implements
Runnable {
public
TaskLocalRandom() {
ThreadLocalRandom.
current
();
}
@Override
public void
run() {
String name = Thread.
currentThread
().getName();
for
(
int
i =
0
; i <
10
; i++) {
System.
out
.printf(
"%s: %d
\n
"
, name, ThreadLocalRandom.
current
().nextInt(
10
));
}
}
public static void
main(String[] args) {
Thread[] threads =
new
Thread[
3
];
for
(
int
i =
0
; i <
3
; i++) {
TaskLocalRandom task =
new
TaskLocalRandom();
threads[i] =
new
Thread(task);
threads[i].start();
}
}
}
原子变量
原子变量不使用锁或其他同步机制来保护对其值的并发访问,所有操作都基于 CAS原子操作,其保证多线程在同一时间操作一个原子变量而不会反升。当一个线程在对原子变量操作时,如果其它线程试图对同一原子变量执行操作,原子变量的实现类提供一套机制来检查操作是否在一步内完成。一般来说,这个操作先获取变量值,然后在本地改变变量的值,然后试图用这个改变的值去替换之前的值。如果之前的值没有被其它线程改变,就可以执行这个替换操作。否则,方法将再执行这个操作。这个操作成为 CAS原子操作(Compare and Set)
AtomicLong 类
public final void set(long newValue) : 设置给定值
public final long getAndAdd(long delta) : 自动给定指定值到当前值,给定值可以为负数
类似的类还有 AtomicBoolean AtomicInteger 等
public class
Bank
implements
Runnable {
private
Account
account
;
public
Bank(Account account) {
this
.
account
= account;
}
@Override
public void
run() {
for
(
int
i =
0
; i <
10
; i++) {
account
.subtractAmount(
1000
);
}
}
}
public class
Company
implements
Runnable {
private
Account
account
;
public
Company(Account account) {
this
.
account
= account;
}
@Override
public void
run() {
for
(
int
i =
0
; i <
10
; i++) {
account
.addAmount(
1000
);
}
}
}
public class
Account {
private
AtomicLong
balance
;
public
Account() {
balance
=
new
AtomicLong();
}
public long
getBalance() {
return
balance
.get();
}
public void
setBalance(
long
balance) {
this
.
balance
.set(balance);
}
public void
addAmount(
long
amount) {
this
.
balance
.getAndAdd(amount);
}
public void
subtractAmount(
long
amount) {
this
.
balance
.getAndAdd(-amount);
}
public static void
main(String[] args) {
Account account =
new
Account();
account.setBalance(
1000
);
Company company =
new
Company(account);
Thread companyThread =
new
Thread(company);
Bank bank =
new
Bank(account);
Thread bankThread =
new
Thread(bank);
System.
out
.printf(
"Account : Initial Balance: %d
\n
"
, account.getBalance());
companyThread.start();
bankThread.start();
try
{
companyThread.join();
bankThread.join();
System.
out
.printf(
"Account : Final Balance: %d
\n
"
, account.getBalance());
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
原子数组
当实现一个并发应用时,将不可避免地会有多线程共享一个或多个对象的现象,为避免数据不一致问题需要使用同步机制(锁或synchronized关键字) 来保护这些共享属性的访问,但是同步机制存在问题 :
1> 死锁 : 一个线程被阻塞,并且试图获得锁正在被其它线程使用,但其它线程永远不会释放这个锁。这种情况使得应用不会继续执行,永远不会结束
2> 即是只有一个线程访问共享对象,它仍然需要执行必须的代码来获取和释放锁
未解决这种问题,Java引入比较和交换操作(Compare-and-Swap Operation),该操作使用以下三个变量值 :
1> 取得变量值,即变量的旧值
2> 在一个临时变量中修改变量值,即变量的新值
3> 如果上面获得的变量值与当前变量值相等,就用新值替换旧值。如果已有其它线程修改这个变量值,上面获得的变量的旧值就可能与当前变量值不同
采用比较和交换机制不需要使用同步机制,不仅可以避免死锁而且性能更好
Java在原子变量中实现这种机制
AtomicIntegerArray 类
public final int getAndDecrement(int i) : 在指定索引处的元素自动减一
public final int getAndIncrement(int i) : 在指定索引处的元素自动加一
public class
Incrementer
implements
Runnable {
private
AtomicIntegerArray
vector
;
public
Incrementer(AtomicIntegerArray vector) {
this
.
vector
= vector;
}
@Override
public void
run() {
for
(
int
i =
0
; i <
vector
.length(); i++) {
vector
.getAndIncrement(i);
}
}
}
public class
Decrementer
implements
Runnable {
private
AtomicIntegerArray
vector
;
public
Decrementer(AtomicIntegerArray vector) {
this
.
vector
= vector;
}
@Override
public void
run() {
for
(
int
i =
0
; i <
vector
.length(); i++) {
vector
.getAndDecrement(i);
}
}
public static void
main(String[] args) {
final int
THREADS =
100
;
AtomicIntegerArray vector =
new
AtomicIntegerArray(
1000
);
Incrementer incrementer =
new
Incrementer(vector);
Decrementer decrementer =
new
Decrementer(vector);
Thread[] threadIncrementer =
new
Thread[THREADS];
Thread[] threadDecrementer =
new
Thread[THREADS];
for
(
int
i =
0
; i < THREADS; i++) {
threadIncrementer[i] =
new
Thread(incrementer);
threadDecrementer[i] =
new
Thread(decrementer);
threadIncrementer[i].start();
threadDecrementer[i].start();
}
try
{
for
(
int
i =
0
; i < THREADS; i++) {
threadIncrementer[i].join();
threadDecrementer[i].join();
}
}
catch
(InterruptedException e) {
e.printStackTrace();
}
for
(
int
i =
0
, len = vector.length(); i < len; i++) {
if
(vector.get(i) !=
0
) {
System.
out
.println(
"Vector["
+ i +
"] : "
+ vector.get(i));
}
}
System.
out
.println(
"Main: End of the example"
);
}
}