阻塞式集合:这类集合包括添加和移除的数据方法。当集合已满或为空时,被调用的添加或者移除方法就不能立即被执行,那么调用这个方法的线程将被阻塞,一直到该方法可以被成功执行。
非阻塞式集合:这类集合也包括添加和移除的方法,如果方法不能立即被执行,则返回null或抛出异常,但是调用这个方法的线程不会被阻塞。
ConcurrentLinkedQueue:基于链接节点的无限制线程安全队列,此队列命令元素FIFO(先进先出)。这个队列在add(),remove(),poll()都用了cas来保证安全。在iterator()时,如果集合被改变,那么数据可能会不一致。
例子:
ConcurrentLinkedQueue c = new ConcurrentLinkedQueue<>();
c.add("1");
c.add("2");
System.out.println(c.poll());
System.out.println(c.poll());
输出结果:
1
2
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
public class SCollection {
public static void main(String[] args) {
final ConcurrentLinkedQueue c = new ConcurrentLinkedQueue<>();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
for (int j = 0; j < 10000; j++) {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
c.add(i * 10000 + j + "");
}
}
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (int i = 0; i < 3; i++) {
System.out.println("第"+i+"次:");
//只返回当前集合的值,如果这期间集合改变,那么数据会不准确
Iterator iterator = c.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
}
源码:
public boolean offer(E e) {
checkNotNull(e);
final Node newNode = new Node(e);
for (Node t = tail, p = t;;) {
Node q = p.next;
if (q == null) {
// p is last node
if (p.casNext(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
if (p != t) // hop two nodes at a time
casTail(t, newNode); // Failure is OK.
return true;
}
// Lost CAS race to another thread; re-read next
}
else if (p == q)
// We have fallen off list. If tail is unchanged, it
// will also be off-list, in which case we need to
// jump to head, from which all live nodes are always
// reachable. Else the new tail is a better bet.
p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
p = (p != t && t != (t = tail)) ? t : q;
}
}
public E poll() {
restartFromHead:
for (;;) {
for (Node h = head, p = h, q;;) {
E item = p.item;
if (item != null && p.casItem(item, null)) {
// Successful CAS is the linearization point
// for item to be removed from this queue.
if (p != h) // hop two nodes at a time
updateHead(h, ((q = p.next) != null) ? q : p);
return item;
}
else if ((q = p.next) == null) {
updateHead(h, p);
return null;
}
else if (p == q)
continue restartFromHead;
else
p = q;
}
}
}
public boolean remove(Object o) {
if (o == null) return false;
Node pred = null;
for (Node p = first(); p != null; p = succ(p)) {
E item = p.item;
if (item != null &&
o.equals(item) &&
p.casItem(item, null)) {
Node next = succ(p);
if (pred != null && next != null)
pred.casNext(p, next);
return true;
}
pred = p;
}
return false;
}
ConcurrentLinkedDeque:基于链接节点的无界并发的双端队列。并发插入,删除和访问操作安全执行。因为这些deques的异步性质,确定当前的数量,元素需要遍历元素,因此可以报告,如果在遍历期间修改此集合,则结果不准确。也是使用cas来保证线程安全,这个类不仅可以操控头部,也可以操控尾部。
ArrayBlockingQueue:有界的阻塞队列基于数组,此队列命令元素FIFO(先进先出)。一开就要确定容器,确定后无法修改。线程安全基于ReentrantLock。
LinkedBlockingDeque:一个可选的有界阻塞双端队列基于链接节点。此队列命令元素FIFO(先进先出)
LinkedBlockingQueue:一个可选的有界阻塞队列基于链接节点。此队列命令元素FIFO(先进先出)。链接队列通常比基于阵列的队列具有更高的吞吐量,但是大多数并发应用程序的性能预测性较差。
take()和push方法分析:
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
Node node = new Node(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
/*
* Note that count is used in wait guard even though it is
* not protected by lock. This works because count can
* only decrease at this point (all other puts are shut
* out by lock), and we (or some other waiting put) are
* signalled if it ever changes from capacity. Similarly
* for all other uses of count in other wait guards.
*/
while (count.get() == capacity) {
//如果容量满了 就将线程阻塞
notFull.await();
}
enqueue(node);
//得到之前的count的值
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
//如果之前的容量为0 就唤醒线程
signalNotEmpty();
}
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
//如果为0 就调用await的方法 当前方法阻塞
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
//如果之前容量满了,就唤醒notfull
signalNotFull();
return x;
}
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
//唤醒
notFull.signal();
} finally {
putLock.unlock();
}
}
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
PriorityBlockingQueue: 使用的无界阻塞队列基于heap实现。虽然这个队列是逻辑上的无限制,尝试添加可能会因资源耗尽而失败。所有添加进PriorityBlockingQueue的元素必须实现Comparable接口。
PriorityBlockingQueue pbq = new PriorityBlockingQueue<>();
pbq.add(5);
pbq.add(3);
System.out.println(pbq.peek());
输出结果:
3。
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class SPriorityBlockingQueue {
public static void main(String[] args) {
ThreadPoolExecutor tpe = new ThreadPoolExecutor(1, 1,60, TimeUnit.SECONDS, new PriorityBlockingQueue<>());
for(int i=6;i>1;i--){
tpe.execute(new SStudent(i));
}
}
}
class SStudent implements Comparable,Runnable{
private Integer age;
public SStudent(Integer age) {
super();
this.age = age;
}
@Override
public int compareTo(SStudent o) {
if(age > o.getAge()){
return 1;
}
else if(age < o.getAge()){
return -1;
}
return 0;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public void run() {
System.out.println("age:"+age);
}
}
输出结果:
age:6
age:2
age:3
age:4
age:5
第一次需要创建线程所以直接执行了,下面进行加入队列,只要实现了Comparable接口,PriorityBlockingQueue会自动排序。
DelayQueue: 无限制的阻塞队列,其中只能使用元素延迟期满时。如果没有延迟到期,则没有头将返回。当元素出现时到期
方法返回的值较少大于或等于零。
存放到DelayQueue类中的元素必须继承Delayed接口。
例子:
import java.util.Date;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class SCollection2 {
public static void main(String[] args) throws Exception{
DelayQueue dq = new DelayQueue<>();
Date now = new Date();
now.setSeconds(now.getSeconds()+5);
long date1l = now.getTime();
now.setSeconds(now.getSeconds()+5);
long date2l = now.getTime();
dq.add(new DTime(new Date(date2l),"date2"));
dq.add(new DTime(new Date(date1l),"date1"));
System.out.println("date1l: " +date1l);
System.out.println("date2l: "+date2l);
System.out.println("first:"+dq.take().getName());
}
}
class DTime implements Delayed{
private Date myTime;
private String name;
public DTime(Date myTime,String name) {
super();
this.myTime = myTime;
this.name = name;
}
@Override
public int compareTo(Delayed o) {
TimeUnit unit = TimeUnit.NANOSECONDS;
if(this.getDelay(unit) > o.getDelay(unit)){
return 1;
}else if(this.getDelay(unit) < o.getDelay(unit)){
return -1;
}
return 0;
}
//一定要返回NANOSECONDS
@Override
public long getDelay(TimeUnit unit) {
long diff = myTime.getTime() - new Date().getTime();
return unit.convert(diff, TimeUnit.MILLISECONDS);
}
public Date getMyTime() {
return myTime;
}
public void setMyTime(Date myTime) {
this.myTime = myTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
源码分析:
public class DelayQueue extends AbstractQueue
implements BlockingQueue {
private final transient ReentrantLock lock = new ReentrantLock();
//里面有PriorityQueue 这个优先级队列
private final PriorityQueue q = new PriorityQueue();
.....
}
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//把添加的元素 加到这个优先级队列里 让优先级队列进行排序
q.offer(e);
if (q.peek() == e) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
//获取第一个元素
E first = q.peek();
if (first == null)
available.await();
else {
//得到第一个的延时时间
long delay = first.getDelay(NANOSECONDS);
//如果小于0 就返回
if (delay <= 0)
return q.poll();
first = null; // don't retain ref while waiting
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
//线程挂起,这边等待延时时间 这边是Nanos
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
SynchronousQueue :每个插入的操作必须等待另一个操作的相应删除操作线程,反之亦然。同步队列没有任何内部容量,甚至不是一个容量。你不能{@code peek}在同步队列中,因为元素只是当你试图删除它时出现;你不能插入一个元素(使用任何方法),除非另一个线程试图删除它;你不能迭代,因为没有什么可以迭代。这个用来管理线程的。
当使用put()时,如果数据没被取走,那么该线程会一直阻塞。直到被take()才会被释放。offer()只有插入的时候正好被调用才会插入成功,一般会有一个线程在调用take()或poll()方法来等待,不然offer一般都会失败返回null。不要用add()方法 ,一般都会抛出异常。
例子。
public class SynchronousQueueTest {
public static void main(String[] args) {
//true保证生产或消费者线程以FIFO的顺序访问。
SynchronousQueue queue = new SynchronousQueue(true);
new Customer(queue).start();
for (int i = 0; i < 3; ++i) {
new Product(queue).start();
}
}
static class Product extends Thread {
SynchronousQueue queue;
public Product(SynchronousQueue queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
int rand = new Random().nextInt(1000);
System.out.println("Thread Id:" + getId() + " 生产了一个产品:" + rand);
/*
* offer()往queue里放一个element后立即返回,如果碰巧这个element被另一个thread取走了,
* offer方法返回true,认为offer成功;否则返回false。
* 也就是说offer不一定真正的插入的队列中,肯定没成功丢失了
*/
// queue.offer(rand); //注意offer与put方法的区别
try {
/*
* put()往queue放进去一个element以后就一直wait直到有其他thread进来把这个element取走。
*/
TimeUnit.SECONDS.sleep(2);
queue.put(rand);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Customer extends Thread {
SynchronousQueue queue;
public Customer(SynchronousQueue queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
try {
// 线程运行到queue.take()阻塞,直到Product生产一个产品queue.offer。
System.out.println("Thread Id:" + getId() + " 消费了一个产品:" + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------------------------------------------");
}
}
}
}
newCachedThreadPool的分析:
//使用了SynchronousQueue队列来管理线程
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//每次都workQueue.offer 判断是否有空闲的线程
//因为空闲线程会调用take()或poll(keepalivetime.unit)方法等待任务的到来
//如果添加失败说明没有线程空闲就会添加新的线程
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
LinkedTransferQueue:一个无边界的阻塞队列,基于链表实现。这个主要有transfer方法,如果加入的数据没有被消费掉,当前线程会一直阻塞。 tryTransfer(e, timeout, unit) 在一段时间内如果没有被获取就返回false, transfer加入的数据都是在链表尾部,如果队列之前有数据,task会先取之前的数据。
列子:
import java.util.concurrent.LinkedTransferQueue;
public class SLinkedTransferQueue {
public static void main(String[] args) {
LinkedTransferQueue ltq = new LinkedTransferQueue<>();
new Thread(){
public void run() {
try {
System.out.println("开始transfer");
ltq.transfer("1");
System.out.println("结束transfer");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run() {
try {
Thread.sleep(3000);
System.out.println("开始take");
System.out.println("value:"+ltq.take());
System.out.println("结束take");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
}
}