java多线程系列(五)—synchronized ReentrantLock volatile Atomic 原理分析详解编程语言

java多线程系列(五)—synchronized ReentrantLock volatile Atomic 原理分析

前言:如有不正确的地方,还望指正。

Synchronized

原理

  • synchronized关键字是通过字节码指令来实现的

  • synchronized关键字编译后会在同步块前后形成monitorenter和monitorexit两个字节码指令

  • 执行monitorenter指令时需要先获得对象的锁(每个对象有一个监视器锁monitor),如果这个对象没被锁或者当前线程已经获得此锁(也就是重入锁),那么锁的计数器+1。如果获取失败,那么当前线程阻塞,直到锁被对另一个线程释放

  • 执行monitorexit指令时,计数器减一,当为0的时候锁释放

class Test 
{ 
    public int i=1; 
    public void test() 
    { 
        synchronized (this) 
        { 
            i++;     
        } 
} 
}

  • 反编译后结果

java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析详解编程语言

volatile

作用

  • 保证变量对所有的线程的可见性,当一个线程修改了这个变量的值,其他线程可以立即知道这个新值(之所以有可见性的问题,是因为java的内存模型)

原理

  • 所有变量都存在主内存,每条线程有自己的工作内存,工作内存保存了被该线程使用的变量的主内存副本拷贝

  • 线程对变量的所有操作都必须在工作内存中进行,不能直接读写主内存的变量,也就是必须先通过工作内存

  • 一个线程不能访问另一个线程的工作内存

  • volatile保证了变量更新的时候能够立即同步到主内存,使用变量的时候能立即从主内存刷新到工作内存,这样就保证了变量的可见性

  • 实际上是通过内存屏障来实现的。语义上,内存屏障之前的所有写操作都要写入内存;内存屏障之后的读操作都可以获得同步屏障之前的写操作的结果。

java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析详解编程语言

Atomic

作用

  • 当有多个线程同时对单个(包括基本类型及引用类型)变量进行操作时,具有排他性,即当多个线程同时对该变量的值进行更新时,仅有一个线程能成功,而未成功的线程可以像自旋锁一样,继续尝试,一直等到执行成功。

原理

  • CAS操作(compare and swap 对比和设置),是通过一个cpu指令实现的,这个指令是一个原子指令,指令有3个操作数ABC,A为内存位置,B为预期值,C为新值,如果A符合旧预期值B,那么用V更新A的值,如果不符合就不更新,这个过程是原子操作

  • 所以我们并没有通过代码来实现同步,而是通过硬件级别的cpu指令来实现的,并不像synchronized一样阻塞线程

//加一并返回值 
public final int incrementAndGet() { 
        for (;;) { 
            int current = get(); 
            int next = current + 1; 
            if (compareAndSet(current, next)) 
                return next; 
        } 
   } 
 
//返回CAS操作成功与否 
public final boolean compareAndSet(int expect, int update) { 
        //根据变量在内存中的偏移地址valueOffset获取原值,然后和预期值except进行比,如果符合,用update值进行更新,这个过程是原子操作 
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 
    }

  • 如果此时有两个线程,线程A得到current值为1,线程B得到current值也为2,此时线程A执行CAS操作,成功将值改为2,而此时线程B执行CAS操作,发现此时内存中的值并不是读到current值1,所以返回false,此时线程B继续进行循环,最后成功加1

Lock

作用

  • 显式加锁

原理

  • 通过同步器AQS(AbstractQueuedSynchronized类)来实现的,AQS根本上是通过一个双向队列来实现的

  • 线程构造成一个节点,一个线程先尝试获得锁,如果获取锁失败,就将该线程加到队列尾部

  • 非公平锁的lock方法,调用的sync(NonfairSync和fairSync的父类)的lock方法

 public ReentrantLock(boolean fair) { 
        sync = fair ? new FairSync() : new NonfairSync(); 
    } 
 
// ReentrantLock的lock方法 
public void lock() { 
        sync.lock(); 
    }

  • NonfairSync的lock方法,acquire的是Sync的父类AQS的acquire方法

 final void lock() { 
            //如果当前同步状态为0(锁未被占有),CAS操作设置同步状态,设置成功的话当前线程获得锁(如果此时是公平锁,那么不会执行compareAndSetState方法,直接acuire排队) 
            if (compareAndSetState(0, 1)) 
                setExclusiveOwnerThread(Thread.currentThread()); 
            //否则调用AQS的acquire方法 
            else 
                acquire(1); 
        }

 //CAS设置锁的状态 
 protected final boolean compareAndSetState(int expect, int update) { 
        // See below for intrinsics setup to support this 
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update); 
    }

  • AQS的acquire方法

//尝试获得锁,如果获取失败,将节点加入到尾节点 
public final void acquire(int arg) { 
        if (!tryAcquire(arg) && 
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
            selfInterrupt(); 
    }

  • tryAcquire方法,尝试获得锁

final boolean nonfairTryAcquire(int acquires) { 
     
    final Thread current = Thread.currentThread(); 
    //获取state变量值 
    int c = getState(); 
    if (c == 0) { //如果没有线程占用锁 
        if (compareAndSetState(0, acquires)) { 
            //设置当前线程获得锁 
            setExclusiveOwnerThread(current); 
            return true; 
        } 
    } else if (current == getExclusiveOwnerThread()) { //当前线程已经占用该锁 
        int nextc = c + acquires; 
        if (nextc < 0) // overflow 
            throw new Error("Maximum lock count exceeded"); 
        // 更新state值为新的重入次数 
        setState(nextc); 
        return true; 
    } 
    //获取锁失败 
    return false; 
}

  • 如果获取锁失败,将节点加入尾节点

private Node addWaiter(Node mode) { 
         Node node = new Node(Thread.currentThread(), mode); 
         // Try the fast path of enq; backup to full enq on failure 
         Node pred = tail; 
            //如果尾节点不为空 
         if (pred != null) { 
             node.prev = pred; 
                //此时可能同时有其他线程插入,再进行判断(通过CAS),如果没有,将节点设置为尾节点 
             if (compareAndSetTail(pred, node)) { 
                 pred.next = node; 
                 return node; 
             } 
         } 
            //如果节点为空或者节点不为空并且有其他线程插入(CAS返回false),执行enq方法 
         enq(node); 
         return node; 
     }

  • 如果节点为空或者节点不为空并且有其他线程插入(CAS返回false),执行enq

//通过自旋进行设置 
private Node More enq(final Node node) { 
         for (;;) { 
             Node t = tail; 
             if (t == null) { // Must initialize 
                 if (compareAndSetHead(new Node())) 
                     tail = head; 
             } else { 
                 node.prev = t; 
                 if (compareAndSetTail(t, node)) { 
                     t.next = node; 
                     return t; 
                 } 
             } 
         } 
     }

  • 进入队列的线程尝试获得锁

 
  final boolean acquireQueued(final Node node, int arg) { 
    boolean failed = true; //是否成功获取锁 
    try { 
        boolean interrupted = false; //线程是否被中断过 
        for (;;) { 
            final Node p = node.predecessor(); //获取前驱节点 
            //如果前驱是head尝试获锁 
            if (p == head && tryAcquire(arg)) { 
                setHead(node); // 获取成功,将当前节点设置为head节点 
                p.next = null; // 原head节点出队 
                failed = false;  
                return interrupted; //返回是否被中断过 
            } 
            // 前节点不是头节点或者获取失败,判断是否可以挂起 
            if (shouldParkAfterFailedAcquire(p, node) && 
                    parkAndCheckInterrupt()) 
                // 线程若被中断,设置interrupted为true 
                interrupted = true; 
        } 
    } finally { 
        if (failed) 
            cancelAcquire(node); 
    } 
}

  • 线程是否可以挂起

 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { 
    //前驱节点的状态 
    int ws = pred.waitStatus; 
    if (ws == Node.SIGNAL) 
        // 前驱节点状态为signal(此节点线程结束后唤醒下一个节点线程) 
        return true; 
    //如果 前驱节点状态为CANCELLED(线程已经被取消) 
    if (ws > 0) { 
        // 删除cancelled状态的节点 
        do { 
            node.prev = pred = pred.prev; 
        } while (pred.waitStatus > 0); 
        pred.next = node; 
    } else { 
        // 将前驱节点的状态设置为SIGNAL 
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL); 
    } 
    return false; 
}

  • 挂起当前线程,返回线程中断状态并重置

  private final boolean parkAndCheckInterrupt() { 
    LockSupport.park(this); 
    return Thread.interrupted(); 
}

作者:jiajun 出处: http://www.cnblogs.com/-new/

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/15335.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论