a. Volatile 关键字:对 Volatile 域的写入操作 happens-before 于每个后续对同一 Volatile 的读操作。
并不保证线程安全。Volatile应用于多核系统的多线程应用中,因为每个cpu都有自己的缓存,并不实时更新到主内存。 多线程环境下,a线程从主内存中读到的数据可能是更改前的,因为b线程更改的数据还在其他核的缓存区。 Volatile声明的变量可以防止这一情况的发生,读操作必然是写操作写到主内存后的数据。b.读写锁 - java.util.concurrent.locks.ReentrantReadWriteLock (1)readLock() , 进入条件:(a)没有其他线程的写锁 (b) 没有写请求或者有写请求,但调用线程和持有锁的线程是同一个 (2)writeLock() ,进入条件:(a)没有其他线程的读锁 (b) 没有其他线程的写锁 简单地理解,读锁是共享锁,写锁是排它锁。c.提高并发量的线程安全工具-ConcurrentHashMap (1)穿插 - HashMap的数据结构 (a)大体结构:散列表(数组) + 链表(1.8后是链表或红黑树) (b)根据key算hash,hashcode是根据地址进行高低位混合运算。根据hashcode的值作为下标插入桶中(就是插入数组中),如果已经有元素, 组装成为链表,新加入的元素作为表头,元素数量大于8个时,组装成红黑树(1.8+)。 (c)散列表的大小都是2^n,当75%的位置都有元素时,会触发resize,会把散列表扩容一倍,所有元素重排。 (2)ConcurrentHashMap的数据结构 (a)先分为16 个 Segment 对象的数组(默认),每个Segment各自维护自己的散列表。 运行锁机制的是Segment对象(继承于 ReentrantLock 类)。理想状况下允许16个线程并发。 (b)put操作。 <1>先算hash,根据hash找到对应的Segment,再在Segment里面加锁进行put操作。 <2>因为加锁在Segment里面,理想情况下,可以支持 16 个线程执行并发写操作。 (c)remove操作。 remove操作不删除原有元素,而是组建一条新的链表到待删除元素的下一元素,当然新的链表是没有待删除元素的。 旧的链表是没有头的,故新的get操作不会进入旧链表。 (d)get操作相关。 get可以在put和remove时正常地读而没有异常产生(因为remove不删除原有元素)。 如果get到null值,说明发生resize,加锁再读一次(ConcurrentHashMap不允许null值)。
2018.12.08