并发编程从零开始(六)-BlockingDeque+CopyOnWrite 5.2 BlockingDeque BlockingDeque定义了一个阻塞的双端队列接口: 该接口继承了BlockingQueue接口,同时增加了对应的双端队列操作接口.该接口只有一个实现,就是LinkedBlockingDeque,其核心数据结构如下所示,是一个双向链表. 对应的实现原理,和LinkedBlockingQueue基本一样,只是LinkedBlockingQueue是单向链表,而LinkedBlocki…
并发编程从零开始(八)-ConcurrentHashMap 5.5 ConcurrentHashMap HashMap通常的实现方式是"数组+链表",这种方式被称为"拉链法".ConcurrentHashMap在这个基本原理之上进行了各种优化. 首先是所有数据都放在一个大的HashMap中:其次是引入了红黑树. 其原理如下图所示: 如果头节点是Node类型,则尾随它的就是一个普通的链表:如果头节点是TreeNode类型,它的后面就是一颗红黑树,TreeNode是No…
并发编程从零开始(九)-ConcurrentSkipListMap&Set CAS知识点补充: 我们都知道在使用 CAS 也就是使用 compareAndSet(current,next)方法进行无锁自加或者更换栈的表头之类的问题时会出现ABA问题. Java中使用 AtomicStampedReference 来解决 CAS 中的ABA问题,它不再像一般原子类中的 compareAndSet 方法一样只比较内存中的值也当前值是否相等,而且先比较引用是否相等,然后比较值是否相等,此外还会比对版本…
并发编程从零开始(十一)-Atomic类 7 Atomic类 7.1 AtomicInteger和AtomicLong 如下面代码所示,对于一个整数的加减操作,要保证线程安全,需要加锁,也就是加synchronized关键字. 但有了Concurrent包的Atomic相关的类之后,synchronized关键字可以用AtomicInteger代替,其性能更好,对应的代码变为: AtomicInteger的 getAndIncrement() 方法和 getAndDecrement() 方法都调…
并发编程从零开始(十二)-Lock与Condition 8 Lock与Condition 8.1 互斥锁 8.1.1 锁的可重入性 "可重入锁"是指当一个线程调用 object.lock()获取到锁,进入临界区后,再次调用object.lock(),仍然可以获取到该锁.显然,通常的锁都要设计成可重入的,否则就会发生死锁. synchronized关键字,就是可重入锁.在一个synchronized方法method1()里面调用另外一个synchronized方法method2().如果…
并发编程从零开始(十四)-Executors工具类 12 Executors工具类 concurrent包提供了Executors工具类,利用它可以创建各种不同类型的线程池 12.1 四种对比 单线程的线程池: 固定数目线程的线程池: 每接收一个请求,就创建一个线程来执行: 单线程具有周期调度功能的线程池: 多线程,有调度功能的线程池: 12.2 最佳实践 不同类型的线程池,其实都是由前面的几个关键配置参数配置而成的. 在<阿里巴巴Java开发手册>中,明确禁止使用Executors创建线程池…
由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识. 一.内存模型的相关概念 Java内存模型规定所有的变量都是存在主存当中,每个线程都有自己的工作内存.线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作.并且每个线程不能访问其他线程的工作内存. 在java中,执行下面这个语句: int i=3; 执行线程必须先在自己的工作线程中对变量i所在的缓存行进行赋值操作,然后再写入主存当中.而不是直接将…
我们接着上一篇文章继续,本文讲讲解ReentrantLock 公平锁和非公平锁的区别,深入分析 AbstractQueuedSynchronizer 中的 ConditionObject 公平锁和非公平锁 ReentrantLock 默认采用非公平锁,除非你在构造方法中传入参数 true . public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ?…
原子性 原子是最小单元.不可再分的意思.原子性是指某个操作在获取CPU时间时,要么就给它足够时间,让这个操作执行完,要么就不执行这个操作,执行时不能出现上下文切换(把CPU时间从一个线程分配到另一个线程). Java中对变量的读取和赋值都是原子操作,但long.double类型除外,只有使用volatile修饰之后long.double类型的读取和赋值操作才具有原子性.除此之外Java还提供了几个常用的原子类,原子类的方法是具有原子性的方法,也就是说原子类在执行某个方法的过程中不会出现上下文切换…
大家使用多线程无非是为了提高性能,但如果多线程使用不当,不但性能提升不明显,而且会使得资源消耗更大.下面列举一下可能会造成多线程性能问题的点: 死锁 过多串行化 过多锁竞争 切换上下文 内存同步 下面分别解析以上性能隐患 死锁 关于死锁,我们在学习操作系统的时候就知道它产生的原因和危害,这里就不从原理上去累述了,可以从下面的代码和图示重温一下死锁产生的原因: public class LeftRightDeadlock { private final Object left = new Obje…