java并发编程(四)
- 2020 年 3 月 11 日
- 笔记
根据课程大纲整理资料。

21:volatile关键字的可见性
volatile 解决的是多核CPU带来的缓存与CPU之间数据的可见性,实现禁止指令重排

volatile即内存屏障,可保证一段内存中一个变量的原子性,原生类型都是原子性的。所以java中 volatile long,volatile double都是线程安全的。
22:volatile与static的关系
volatile 是保证从主内存加载到线程工作内存的值是最新的,此时并未加载并刷新到主内存中,static表示当前类的实例在主内存中用的是同一份。只能保证变量的来源,volatile, 声明变量值的一致性;static,声明变量的唯一性。
23:Atomic的原子性
原生类型都是原子性,以AtomicInteger为例
{@link java.util.concurrent.atomic} package specification for * description of the properties of atomic variables
getAndIncrement()方法会原子性的进行增量操作把当前值加1。Atomic要跟CAS放在一起
public final int getAndAdd(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta); }
24:CAS Compare and Swap
private static final Unsafe unsafe
private volatile int value;
unsafe类是CAS的核心类,是由C语言native方法来访问的
public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
其中getAndAddInt方法,this表示当前对象valueoffset表示内存中的偏移地址,delta是当前value增加的变量
CAS即比较当前值与预设值,交换并增加,如果与预想一致就交换,否则再次自旋,所以带来循环开销问题,进而引来ABA问题。
25:Threadlocal
这个类提供线程局部变量。 这些变量与其正常的对应方式不同,因为访问一个的每个线程(通过其get
或set
方法)都有自己独立初始化的变量副本。 ThreadLocal
实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或事务ID)
1.如果多个线程访问同一个共享Threadlocal变量,是保证线程隔离的,A,B,C访问的即Threadlocal的变量副本
2.与线程状态相关,单个线程内,该线程持有该资源变量是私有的
Threadlocal并不是为了保证线程安全,而是为了在线程内方便共享变量跨方法的传递
经典用法:@Transactional事务的处理,利用其get方法获取Entry[],为每一个线程提供变量的副本,实现线程的隔离,每个线程只访问自己的数据。
26:InheritableThreadLocal与Threadlocal
首先InheritableThreadLocal继承了Threadlocal,Inheritable本身即是继承的意思,
/** * This class extends <tt>ThreadLocal</tt> to provide inheritance of values * from parent thread to child thread: when a child thread is created, the * child receives initial values for all inheritable thread-local variables * for which the parent has values. Normally the child's values will be * identical to the parent's; however, the child's value can be made an * arbitrary function of the parent's by overriding the <tt>childValue</tt> * method in this class. * * <p>Inheritable thread-local variables are used in preference to * ordinary thread-local variables when the per-thread-attribute being * maintained in the variable (e.g., User ID, Transaction ID) must be * automatically transmitted to any child threads that are created. * * @author Josh Bloch and Doug Lea * @see ThreadLocal * @since 1.2 */ public class InheritableThreadLocal<T> extends ThreadLocal<T> { ... }
创建子线程时,子线程接收所有可继承线程局部变量的初始值。
说实话第一次听到跟他比较,比如ThreadLocal VS FastThreadlocal,既然是继承,大多是为了扩展父类方法,解决其threadlocal.get()得不到其新线程中的值,
推荐博客: https://blog.csdn.net/ni357103403/article/details/51970748
27:Unsafe类详解
Unsafe类除了CAS理解相关以外,确实了解的不多,这里推荐美团技术博客
Java魔法类:Unsafe应用解析 https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
它属于native.C语言编写的类,在cas,netty中都有应用。这里提到了线程调度可用于线程挂起,真实调用了park()方法实现。
28:同步类容容器
CopyOnWriteArraySet
ConcurentSkipListSet
ConcurrentHashMap
与 ConcurrentSkipListMap
ConcurrentHashMap 加锁
ConcurrentSkipListMap 不需要加锁,浪费空间
29:ConcurrentHashMap
Hashmap是并发线程不安全的,其无法解决共享变量的安全问题,那么解决的方法为
1.替换为Hashtable
2.Collections.synchronizedMap()
3.ConcurrentHashMap
其中Hashtable之所以安全因为其put,get等大多数方法为synchronized
修饰的,但并发场景下锁的粒度太重,性能降低
Collections.synchronizedMap()
public static void main(String[] args) { HashMap<String, String> map = new HashMap<>(); Map<String, String> synchronizedMap = Collections.synchronizedMap(map); }
其put方法get方法也都是synchronized
public V put(K key, V value) { synchronized (mutex) {return m.put(key, value);} } public V get(Object key) { synchronized (mutex) {return m.get(key);} }
ConcurrentHashMap
1.7是锁分段 segment 16,隔离级别太大,有很多空间就浪费了,太小就段内的元素过多
1.8以后是cas算法C语言写得,无锁算法,put添加的时候,链表+红黑树
put方法(无锁添加)

29:无阻塞队列ConcurrentLinkedQueue
An unbounded thread-safe {@linkplain Queue queue} based on linked nodes. * This queue orders elements FIFO (first-in-first-out). public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> implements Queue<E>, java.io.Serializable { }
一个为绑定线程安全的队列,队列的元素先进先出
看其节点的内部方法
/** * Constructs a new node. Uses relaxed write because item can * only be seen after publication via casNext. */ Node(E item) { UNSAFE.putObject(this, itemOffset, item); } 构造新节点。使用轻松写入,因为item可以仅在通过casNext发布后才能看到
那其实还是跟Unsafe类离不开。
推荐博客http://ifeve.com/concurrentlinkedqueue/
我没有梦见你, 但我梦见了糖葫芦, 醒来我突然觉得我好久没吃过糖葫芦了 然后就想跟你讲一下 可能 你和糖葫芦一样甜吧
