zoukankan      html  css  js  c++  java
  • 《Java并发编程实战》第十五章 原子变量与非堵塞同步机制 读书笔记



    一、锁的劣势

    锁定后假设未释放。再次请求锁时会造成堵塞,多线程调度通常遇到堵塞会进行上下文切换,造成很多其它的开销。
    在挂起与恢复线程等过程中存在着非常大的开销,而且通常存在着较长时间的中断。
    锁可能导致优先级反转。即使较高优先级的线程能够抢先运行,但仍然须要等待锁被释放,从而导致它的优先级会降至低优先级线程的级别。


    二、硬件对并发的支持

    处理器填写了一些特殊指令,比如:比較并交换、关联载入/条件存储。

    1 比較并交换
    CAS的含义是:“我觉得V的值应该为A。假设是,那么将V的值更新为B,否则不须要改动告诉V的值实际为多少”。

    CAS是一项乐观锁技术。


    模拟CAS操作样例:
    @ ThreadSafe
    public class SimulatedCAS {
           @ GuardeBy( "this") private int value ;
          
           public synchronized int get(){
                 return value ;
          }
          
           public synchronized int compareAndSwap( int expectedValue, int newValue){
                 int oldValue = value ;
                 if (oldValue == expectedValue)
                       value = newValue;
                 return oldValue;
          }
          
           public synchronized boolean compareAndSet( int expectedValue, int newValue){
                 return (expectedValue == compareAndSwap(expectedValue, newValue));
          }
    }


    2 非堵塞的计数器
    基于CAS实现的非堵塞计数器
    @ ThreadSafe
    public class CasCounter {
           private SimulatedCAS value ;
          
           public int getValue(){
                 return value .get();
          }
          
           public int increment(){
                 int v;
                 do {
                      v = value .get();
                } while (v != value .compareAndSwap(v, v + 1));
                 return v + 1;
          }
    }


    CAS的主要缺点是:它将使调度者处理竞争问题(通过重试、回退、放弃),而在使用锁中能自己主动处理竞争问题(线程在获得锁之前将一直堵塞)。

    3 JVM对CAS的支持

    java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。
    AtomicBoolean 能够用原子方式更新的 boolean 值。

    AtomicInteger 能够用原子方式更新的 int 值。 AtomicIntegerArray 能够用原子方式更新其元素的 int 数组。 AtomicIntegerFieldUpdater<T> 基于反射的有用工具,能够对指定类的指定 volatile int 字段进行原子更新。

    AtomicLong 能够用原子方式更新的 long 值。 AtomicLongArray 能够用原子方式更新其元素的 long 数组。 AtomicLongFieldUpdater<T> 基于反射的有用工具,能够对指定类的指定 volatile long 字段进行原子更新。 AtomicMarkableReference<V> AtomicMarkableReference 维护带有标记位的对象引用,能够原子方式对其进行更新。

    AtomicReference<V> 能够用原子方式更新的对象引用。

    AtomicReferenceArray<E> 能够用原子方式更新其元素的对象引用数组。 AtomicReferenceFieldUpdater<T,V> 基于反射的有用工具。能够对指定类的指定 volatile 字段进行原子更新。 AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用。能够用原子方式对其进行更新。



    三、原子变量类

    1 原子变量是一种“更好的volatile”
    通过CAS来维持包括多个变量的不变性条件样例:
    import java.util.concurrent.atomic.AtomicReference;
    
    public class CasNumberRange {
           private static class IntPair{
                 final int lower ; // 不变性条件: lower <= upper
                 final int upper ;
                
                 public IntPair( int lower, int upper) {
                       this .lower = lower;
                       this .upper = upper;
                }
          }
          
           private final AtomicReference<IntPair> values =
                       new AtomicReference<IntPair>( new IntPair(0, 0));
          
           public int getLower(){
                 return values .get(). lower;
          }
          
           public int getUpper(){
                 return values .get(). upper;
          }
          
           public void setLower( int i){
                 while (true ){
                      IntPair oldv = values .get();
                       if (i > oldv.upper ){
                             throw new IllegalArgumentException( "Cant't set lower to " + i + " > upper");
                      }
                      IntPair newv = new IntPair(i, oldv.upper );
                       if (values .compareAndSet(oldv, newv)){
                             return ;
                      }
                }
          }
           // 对setUpper採用相似的方法
    }



    2 性能比較:锁与原子变量
    使用ReentrantLock、AtomicInteger、ThreadLocal比較,通常情况下效率排序是ThreadLocal > AtomicInteger > ReentrantLock。

    四、非堵塞算法

    1 非堵塞的栈
    import java.util.concurrent.atomic.AtomicReference;
    
    public class ConcurrentStack<E> {
           private AtomicReference<Node<E>> top = new AtomicReference<ConcurrentStack.Node<E>>();
          
           public void push(E item){
                Node<E> newHead = new Node<E>(item);
                Node<E> oldHead;
                
                 do {
                      oldHead = top .get();
                      newHead. next = oldHead;
                } while (!top .compareAndSet(oldHead, newHead));
          }
          
           public E pop(){
                Node<E> oldHead;
                Node<E> newHead;
                 do {
                      oldHead = top .get();
                       if (oldHead == null) {
                             return null ;
                      }     
                      newHead = oldHead. next ;
                } while (!top .compareAndSet(oldHead, newHead));
                 return oldHead.item ;
          }
          
           private static class Node<E>{
                 public final E item;
                 public Node<E> next ;
                
                 public Node(E item){
                       this .item = item;
                }
          }
    }



    2 非堵塞的链表
    CAS基本使用模式:在更新某个值时存在不确定性。以及在更新失败时又一次尝试。
    import java.util.concurrent.atomic.AtomicReference;
    
    @ ThreadSafe
    public class LinkedQueue<E> {
           private static class Node<E>{
                 final E item;
                 final AtomicReference<Node<E>> next;
                
                 public Node(E item, Node<E> next){
                       this .item = item;
                       this .next = new AtomicReference<Node<E>>(next);
                }
          }
          
           private final Node<E> dummy = new Node<E>( null , null );
           private final AtomicReference<Node<E>> head =
                                         new AtomicReference<Node<E>>(dummy);
           private final AtomicReference<Node<E>> tail =
                                         new AtomicReference<Node<E>>(dummy);
          
           public boolean put(E item){
                Node<E> newNode = new Node<E>(item, null);
                 while (true ){
                      Node<E> curTail = tail.get();
                      Node<E> tailNext = curTail.next.get();
                       if (curTail == tail.get()){
                             if (tailNext != null){
                                   // 队列处于中间状态,推进尾节点
                                  tail.compareAndSet(curTail, tailNext);
                            } else {
                                   // 处于稳定状态, 尝试插入新节点
                                   if (curTail.next.compareAndSet( null, newNode)){
                                         // 插入操作成功。尝试推进尾节点
                                        tail.compareAndSet(curTail, tailNext);
                                         return true ;
                                  }
                            }
                      }
                }
          }
    }



    3 原子的域更新器
    原子的域更新器类表示有volatile域的一种基于反射的“视图”。从而可以在已有的volatile域上使用CAS
    private static class Node<E>{
                 private final E item;
                 private volatile Node<E> next;
                
                 public Node(E item){
                       this.item = item;
                }
          }
    
    private static AtomicReferenceFieldUpdater<Node, Node> nextUpdater
                = AtomicReferenceFieldUpdater.newUpdater(Node.class , Node.class , "next" );



    4 ABA问题
    处理V的值首先由A变成B,再由B变成A的问题。




  • 相关阅读:
    JAVA多线程2 锁
    IE8标准模式下VML不能显示问题
    JAVA多线程1
    JAVA判断32位还是64位,调用不同的DLL
    JNA调用DLL
    如何提高执行力
    httpClient多线程请求
    【NodeJS】安装
    [转载]一个项目涉及到的50个Sql语句(整理版)
    resultMap中的collection集合出现只能读取一条数据的解决方法
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/6888900.html
Copyright © 2011-2022 走看看