互斥锁:
多个线程中只有一个线程才能持有锁
共享锁:
该锁可被多个线程共有,典型的就是ReentrantReadWriteLock里的读锁,它的读锁是可以被共享的,但是它的写锁确每次只能被独占
互斥锁只允许一个线程访问,共享锁可以被多个线程访问,在读锁中对于提升了效率和性能
有界队列:固定大小的队列
•ArrayBlockingQueue 基于数组实现的阻塞队列
•LinkedBlockingQueue 其实也是有界队列,但是不设置大小时就时Integer.MAX_VALUE,内部是基于链表实现的
•ArrayBlockingQueue 实现简单,表现稳定,添加和删除使用同一个锁,通常性能不如后者
•LinkedBlockingQueue 添加和删除两把锁是分开的,所以竞争会小一些
•SynchronousQueue 比较奇葩,内部容量为零,适用于元素数量少的场景,尤其特别适合做交换数据用,内部使用队列来实现公平性的调度,使用栈来实现非公平的调度,在Java6时替换了原来的锁逻辑,使用CAS代替了
无界队列:没有设置固定大小的队列,可以直接入列,直到溢出
•ConcurrentLinkedQueue 无锁队列,底层使用CAS操作,通常具有较高吞吐量,但是具有读性能的不确定性,弱一致性——不存在如ArrayList等集合类的并发修改异常,通俗的说就是遍历时修改不会抛异常
•PriorityBlockingQueue 具有优先级的阻塞队列
•DelayedQueue 延时队列,使用场景
•缓存:清掉缓存中超时的缓存数据
•任务超时处理
•补充:内部实现其实是采用带时间的优先队列,可重入锁,优化阻塞通知的线程元素leader
•LinkedTransferQueue 简单的说也是进行线程间数据交换的利器,在SynchronousQueue 中就有所体现,并且并发大神 Doug Lea 对其进行了极致的优化,使用15个对象填充,加上本身4字节,总共64字节就可以避免缓存行中的伪共享问题
阻塞队列的概念
一个指定长度的队列,如果队列满了,添加新元素的操作会被阻塞等待,直到有空位为止。同样,当队列为空时候,请求队列元素的操作同样会阻塞等待,直到有可用元
主要优点
BlockingQueue提供了一个正确的,线程安全的实现。开发人员已经实施了这个功能多年,但要正确使用它却非常棘手。现在,运行时已经由并发专家开发,审查和维护了一个实现。
队列的“阻塞”性质有两个好处。首先,在添加元素时,如果队列容量有限,则内存消耗也是有限的。另外,如果队列消费者远远落后于生产者,则生产者自然会受到限制,因为他们必须等待添加元素。从队列中获取元素时,主要优点是简单; 永远等待是微不足道的,正确地等待指定的超时只是稍微复杂一点。
Demo 使用线程池创建一个线程去进行获取ArrayBlockingQueue里面的数据(take如果没有获取到数据会将当前线程阻塞,若有数据进来了则会唤醒)
public static void main( String[] args ) throws InterruptedException {
BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(10);
blockingQueue.put("Test");
blockingQueue.take(); //当队列为空的时候会一直阻塞知道有数据才会进行唤醒 //wait/notify | condition .await/signal | Lock
// blockingQueue.iterator();
System.out.println( "Hello World!" );
}
private final ExecutorService executorService= Executors.newSingleThreadExecutor();
ArrayBlockingQueue<String> arrayBlockingQueue=new ArrayBlockingQueue(10);
public void init(){ //不断消费队列的线程
executorService.execute(()->{
while(true){
try {
String s=arrayBlockingQueue.take(); //阻塞式
System.out.println("获取数据:"+ s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
public boolean test(){
try {
arrayBlockingQueue.put("ttt");
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
CountDownLatch 工具使用,类似于join的功能
可以通过CountDownLatch 去控制线程的结果和等待
运用了AQS的共享锁机制
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//加入一个双向的队列
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
//抢占共享锁,判断state的值
int r = tryAcquireShared(arg);
if (r >= 0) {
//设置下一个Node为头节点,并且进行传递唤醒(类似多米诺骨牌)
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
//表示当前节点可以往下面进行传递
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
//当为空或者是share状态时
if (s == null || s.isShared())
doReleaseShared();
}
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
Semaphore (信号灯)
通过停车位理解(若为5个停车位,若以停满则后续的进来也是要等待)类似于一种限流的机制
Demo
public class SemaphoreDemo1 {
static class Test extends Thread{
Semaphore semaphore;
int num;
public Test(Semaphore semaphore, int num) {
this.semaphore = semaphore;
this.num = num;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("第"+num+"线程占用一个令牌");
Thread.sleep(3000);
System.out.println("第"+num+"线程释放一个令牌");
semaphore.release(); //释放令牌
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
AtomicDemo
public class AtomicDemo1 {
public static AtomicInteger i = new AtomicInteger();
public static void inc(){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
i.getAndIncrement();
}
public static void main(String[] args) {
for (int j = 0; j < 1000 ; j++) {
new Thread(()->{
AtomicDemo1.inc();
}).start();
}
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("result:"+i.get());
}
}