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

这个类提供线程局部变量。 这些变量与其正常的对应方式不同,因为访问一个的每个线程(通过其getset方法)都有自己独立初始化的变量副本。 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/
我没有梦见你,  但我梦见了糖葫芦,  醒来我突然觉得我好久没吃过糖葫芦了  然后就想跟你讲一下  可能  你和糖葫芦一样甜吧