Java并发编程 --- 同步机制

书接上回,我们提到了线程安全问题,那么肯定需要相应的同步机制了。

Happens-Before原则

JMM为程序中所有的操作定义了一个偏序关系,称之为Happens-Before,如果想保证B线程看到A线程的操作结果,那么A与B之间要满足Happens-Before原则。

image.png

在Happens-Before模型中,线程A的操作对于线程B来说是可见的。

满足的规则

1、程序顺序规则:操作A在操作B之前,那么线程中A操作在B操作之前。

2、监视器锁规则(synchronized)

3、volatile变量规则:对volatile变量写入操作必须在对该变量的读操作之前执行。

4、传递性:如果操作A必须在操作B之前执行,且操作B必须在操作C之前执行,那么操作A必须在操作C之前执行。

volatile

volatile是Java的原语,是一种稍弱的同步机制。volatile保证了可见性和有序性(禁止指令重排序)。

可见性原理

当一个线程对volatile修饰的变量进行写操作时,JMM会发送一条lock前缀指令,并强制让该变量的缓存行写入主内存。并使用MESI协议广播其他线程,该变量已经发生了改变,需要重新获取值。

MESI协议

又名高速缓存一致性协议。作用是为了保证各个CPU缓存数据的一致性。

MESI 协议定义了高速缓存中数据的四种状态:

  1. Modified(M):表示缓存行已经被修改,但还没有被写回主存储器。在这种状态下,只有一个 CPU 能独占这个修改状态。

  2. Exclusive(E):表示缓存行与主存储器相同,并且是主存储器的唯一拷贝。这种状态下,只有一个 CPU 能独占这个状态。

  3. Shared(S):表示此高速缓存行可能存储在计算机的其他高速缓存中,并且与主存储器匹配。在这种状态下,各个 CPU 可以并发的对这个数据进行读取,但都不能进行写操作。

  4. Invalid(I):表示此缓存行无效或已过期,不能使用。

过程

当某个CPU对共享数据进行修改时,它会先将这个数据从E或S修改为M,然后再向其他CPU广播,让他们把这个共享数据状态设置为I。M状态下的CPU,会找机会将修改的数据写会主存储器(我觉得会在I状态读取前去做一个数据修改)。I状态下的CPU,会重新读取新的数据.

有序性实现原理

volatile的有序性实现是通过内存屏障实现的,内存屏障前后禁止重排序优化。

内存屏障

内存屏障是一种硬件级别的同步操作。它能够强制处理器按照特定顺序执行内存操作。

内存屏障有两种:一种是写内存屏障,一种是读内存屏障。

写内存屏障

当线程写入volatile变量时,JMM会使用写屏障,强制后来的其他线程都要在此次完成后再进行写操作。

读内存屏障

读内存屏障主要是为了防止此次读操作之后的写操作被重排序到读操作之前

synchronized

书接上回,我们了解到了volatile是一种稍弱的同步机制,那么我们来了解一些强同步机制的锁---synchronized。

synchronized是一种悲观锁、独占锁。在JVM层面实现的同步机制。它保证了原子性、可见性、有序性

底层原理

image.png

synchronized是在JVM层面实现的,它把一个对象作为锁,当多个线程都以同一个对象为锁,那么就实现了多线程之间的同步。

一些相关信息都存储在锁对象的对象头中。

所以synchronized的内部有啥呢?

1、锁主人区域:这块存储锁主人的线程,主要作用就是为了支持锁重入

2、等待链:当其他线程发现锁被占有只有,就会进入阻塞状态,并进入等待链中。

3、休息室:当线程获得锁对象之后,因为某些特殊原因,调用了wait(),那么就会放弃锁对象,自动进入休息室。等待其他获得锁对象的线程执行notify()/notifyAll()才会唤醒休息室的线程。

synchronized的特点

透过上述原理,我们来讲讲synchronized的特点。

1、可重入,这点是因为有存储锁主人,所以进行锁主人判断即可实现锁重入。

2、非公平锁,为什么是非公平呢,因为会存在这种情况,当一个线程执行完释放锁,那么就会唤醒在等待链中的其他线程争抢锁资源,那如果此时刚好有一个很新鲜(从未进入过等待链)的线程争抢到了锁资源,那么就发生了非公平现象。

锁自旋

这个我们主要探讨的是当线程发现锁资源被其他人占有了之后,线程应该执行哪种策略。

策略如下:

1、哥们我直接去等待链。

2、哥们再等等,万一刚好其他人释放锁资源了呢。

针对上述策略我们来做一个评价:

策略1的方法其实是比较适合在单核CPU的环境下的,因为单核CPU同一时刻只能运行一个线程,那么它肯定得阻塞等待,不去占有持有锁资源的线程的CPU资源。所以单核CPU下,不能锁自旋。

策略2是比较适合在多核CPU的环境下的,这样的优点是,我们通过自旋一段时间等待来减少了一些线程上下文切换的时间开销。

锁升级

synchronized本身是一个比较重量级的锁,性能方面并不是特别好。基于此,所以有了锁升级的场景,让synchronized面对不同的场景需求进行升级。(越升级性能越差)

升级过程:无锁→偏向锁→轻量级锁→重量级锁

偏向锁

思想:偏向于让第一个获取锁对象的线程,赋予他重新获取锁后不需要同步操作。

场景:当synchronized发现好几次都只有同一个线程使用,那么synchronized就会偏向该线程。

轻量级锁

思想:一个对象有多个线程要加锁,但加锁的时间是错开的(没有竞争)或者通过CAS能够获取得到锁,可以使用轻量级锁来优化,轻量级锁对使用者是透明的(不可见)

场景:一个对象有多个线程要加锁,但加锁的时间是错开的(没有竞争)或者通过CAS能够获取得到锁。

重量级锁

那就是synchronized本来的样貌了。

场景:在轻量级锁下,如果存在线程CAS也获取不到锁,那么轻量级锁就会锁膨胀成重量级锁。

ReentrantLock

上述synchronized是JVM层面实现的同步机制,而ReentrantLock是JDK层面实现的同步机制。

ReentrantLock & synchronized

它们两个非常相似,却也有些许不同

DisLike

不同的:

1、可中断:ReentrantLock提供了中断机制,而synchronized没有。

2、获取锁时间:ReentrantLock可以设置获取锁的时间,而synchronized会一直死等。

3、公平锁:ReentrantLock可以设置为公平锁,synchronized不可能。

4、条件变量(休息室):ReentrantLock可以有多个条件变量,而synchronized只有一个。

5、实现:ReentrantLock本质由CAS和volatile实现,synchronized由JVM实现。

6、解锁:ReentrantLock需要自己手动解锁,synchronized执行完同步代码块就会自动解锁。

Like

相同的:

都可重入

两者如何选择(使用场景)

ReentrantLock的危险性比synchronized要高,因为ReentrantLock在使用时,需要手动解锁,而如果我们忘记解锁,代码表面上还能正常运行,但实际上已经埋下了定时炸弹,所以优先推荐用synchronized

当然,在场景需要一些高级功能时:如定时获取、可中断、多条件变量的场景下,要使用ReentrantLock。

可重入原理

针对这一原理,我们得溯源到ReentrantLock的源码。如下是lock()中非公平锁的实现。

final boolean initialTryLock() {
     //获取当前线程
     Thread current = Thread.currentThread();
     if (compareAndSetState(0, 1)) { 
         setExclusiveOwnerThread(current);
         return true;
       
        //可重入的原理如下:在已被加锁的前提下,它会判断当前想要加锁的是否是锁主人线程,如果是,即可再次加锁,加锁即是让state++
     } else if (getExclusiveOwnerThread() == current) {
         //加锁
         int c = getState() + 1;
         //防溢出
         if (c < 0) // overflow
             throw new Error("Maximum lock count exceeded");
         setState(c);
         return true;
      } else
         return false;
}

可以看到,可重入的原理特别简单,就是再加锁的前提下,去判断当前线程是否是锁主人线程即可。

可打断原理

public void lockInterruptibly():获得可打断的锁

  • 如果没有竞争此方法就会获取 lock 对象锁

  • 如果有竞争就进入阻塞队列,可以被其他线程用 interrupt 打断

  • 注意:如果是不可中断模式,那么即使使用了 interrupt 也不会让等待状态中的线程中断

/**
 * 可打断
 */
public class test5 {
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("尝试获取锁");
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                System.out.println("没有获取得到锁");
                return;
                //throw new RuntimeException(e);
            }
            try {
                System.out.println("获取到锁");
            } finally {
                lock.unlock();
            }

        });
        lock.lock();
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("主线程进行打断锁");
        thread.interrupt();
    }
}
实现原理
  • 不可打断模式:即使它被打断,仍会驻留在 AQS 阻塞队列中,一直要等到获得锁后才能得知自己被打断

public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//阻塞等待
// 如果acquireQueued返回true,打断状态 interrupted = true
selfInterrupt();
}
static void selfInterrupt() {
// 知道自己被打断了,需要重新产生一次中断完成中断效果
Thread.currentThread().interrupt();
}



  ```Java
final boolean acquireQueued(final Node node, int arg) {    
    try {        
        boolean interrupted = false;        
        for (;;) {            
            final Node p = node.predecessor();            
            if (p == head && tryAcquire(arg)) {                
                setHead(node);                
                p.next = null; // help GC                
                failed = false;                
                // 还是需要获得锁后, 才能返回打断状态
                return interrupted;            
            }            
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()){
                // 条件二中判断当前线程是否被打断,被打断返回true,设置中断标记为 true,【获取锁后返回】
                interrupted = true;  
            }                  
        } 
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
 private final boolean parkAndCheckInterrupt() {    
     // 阻塞当前线程,如果打断标记已经是 true, 则 park 会失效
     LockSupport.park(this);    
     // 判断当前线程是否被打断,清除打断标记,被打断返回true
     return Thread.interrupted();
 }
  • 可打断模式:AbstractQueuedSynchronizer#acquireInterruptibly,被打断后会直接抛出异常

public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg) {
// 被其他线程打断了直接返回 false
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
// 没获取到锁,进入这里
doAcquireInterruptibly(arg);
}

  ```Java
private void doAcquireInterruptibly(int arg) throws InterruptedException {
    // 返回封装当前线程的节点
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            //...
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                // 【在 park 过程中如果被 interrupt 会抛出异常】, 而不会再次进入循环获取锁后才完成打断效果
                throw new InterruptedException();
        }    
    } finally {
        // 抛出异常前会进入这里
        if (failed)
            // 取消当前线程的节点
            cancelAcquire(node);
    }
}
// 取消节点出队的逻辑
private void cancelAcquire(Node node) {
  // 判空
  if (node == null)
      return;
  // 把当前节点封装的 Thread 置为空
  node.thread = null;
  // 获取当前取消的 node 的前驱节点
  Node pred = node.prev;
  // 前驱节点也被取消了,循环找到前面最近的没被取消的节点
  while (pred.waitStatus > 0)
      node.prev = pred = pred.prev;
  
  // 获取前驱节点的后继节点,可能是当前 node,也可能是 waitStatus > 0 的节点
  Node predNext = pred.next;
  
  // 把当前节点的状态设置为 【取消状态 1】
  node.waitStatus = Node.CANCELLED;
  
  // 条件成立说明当前节点是尾节点,把当前节点的前驱节点设置为尾节点
  if (node == tail && compareAndSetTail(node, pred)) {
      // 把前驱节点的后继节点置空,这里直接把所有的取消节点出队
      compareAndSetNext(pred, predNext, null);
  } else {
      // 说明当前节点不是 tail 节点
      int ws;
      // 条件一成立说明当前节点不是 head.next 节点
      if (pred != head &&
          // 判断前驱节点的状态是不是 -1,不成立说明前驱状态可能是 0 或者刚被其他线程取消排队了
          ((ws = pred.waitStatus) == Node.SIGNAL ||
           // 如果状态不是 -1,设置前驱节点的状态为 -1
           (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
          // 前驱节点的线程不为null
          pred.thread != null) {
          
          Node next = node.next;
          // 当前节点的后继节点是正常节点
          if (next != null && next.waitStatus <= 0)
              // 把 前驱节点的后继节点 设置为 当前节点的后继节点,【从队列中删除了当前节点】
              compareAndSetNext(pred, predNext, next);
      } else {
          // 当前节点是 head.next 节点,唤醒当前节点的后继节点
          unparkSuccessor(node);
      }
      node.next = node; // help GC
  }

条件变量

synchronized的条件变量,是当条件不满足时进入WaitSet等待,只有一个;ReentrantLock可以支持多个条件变量。

ReentrantLock 类获取 Condition 对象:public Condition newCondition()

Condition 类 API:

  • void await():当前线程从运行状态进入等待状态,释放锁

  • void signal():唤醒一个等待在 Condition 上的线程,但是必须获得与该 Condition 相关的锁

使用流程:

  • await / signal 前需要获得锁

  • await 执行后,会释放锁进入 ConditionObject 等待

  • await 的线程被唤醒去重新竞争 lock 锁

  • 线程在条件队列被打断会抛出中断异常

  • 竞争 lock 锁成功后,从 await 后继续执行

AQS(AbstractQueuedSynchronizer)

AQS是同步器,ReentrantLock能够实现同步机制,就是这哥们在帮忙。(当然,还有一个重量级嘉宾---Unsafe)

核心思想

AQS的核心思想,如果被请求的资源空闲,那么就设置当前请求资源的线程为锁的主人,并且将资源设置为锁定状态。如果在资源被锁定的情况下,其他线程请求获得资源,那么就需要一些阻塞等待机制,AQS使用的是prak和unpark机制(由Unsafe提供)。并将暂时获取不到锁的线程放到同步队列中。

当锁主人完成了相应操作之后,它需要做三件事:

1、进行资源(state)的释放

2、资源释放完后,设置锁主人为null(表明自己已经不是锁持有者)

3、唤醒CLH后继节点去进行资源的争抢

模式

  • 独占模式是只有一个线程能够访问资源,如 ReentrantLock

  • 共享模式允许多个线程访问资源,如 Semaphore,ReentrantReadWriteLock 是组合式

数据结构/方法

①state

private volatile int state;//共享变量,使用volatile修饰保证线程可见性

ReentrantLock把state当做锁资源,当state为0时,表示可加锁,当state大于0时,表示锁已被占用。

当然,ReentrantLock也是支持可重入的,锁主人线程重入时对state进行+1操作。

②CAS

//原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值)
protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

ReentrantLock通过CAS操作来保证state的线程安全。

③同步队列与条件队列

image.png

其中同步队列为双向链表条件队列为单向链表

结点Node类如下:

static final class Node {
    // 模式,分为共享与独占
    // 共享模式
    static final Node SHARED = new Node();
    // 独占模式
    static final Node EXCLUSIVE = null;        
    // 结点状态
    // CANCELLED,值为1,表示当前的线程被取消
    // SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark
    // CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
    // PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行
    // 值为0,表示当前节点在sync队列中,等待着获取锁
    static final int CANCELLED =  1;
    static final int SIGNAL    = -1;
    static final int CONDITION = -2;
    static final int PROPAGATE = -3;        

    // 结点状态
    volatile int waitStatus;        
    // 前驱结点
    volatile Node prev;    
    // 后继结点
    volatile Node next;        
    // 结点所对应的线程
    volatile Thread thread;        
    // 下一个等待者
    Node nextWaiter;
    
    // 结点是否在共享模式下等待
    final boolean isShared() {
        return nextWaiter == SHARED;
    }
    
    // 获取前驱结点,若前驱结点为空,抛出异常
    final Node predecessor() throws NullPointerException {
        // 保存前驱结点
        Node p = prev; 
        if (p == null) // 前驱结点为空,抛出异常
            throw new NullPointerException();
        else // 前驱结点不为空,返回
            return p;
    }
    
    // 无参构造方法
    Node() {    // Used to establish initial head or SHARED marker
    }
    
    // 构造方法
        Node(Thread thread, Node mode) {    // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }
    
    // 构造方法
    Node(Thread thread, int waitStatus) { // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

条件变量ConditionObject类如下:

// 内部类
public class ConditionObject implements Condition, java.io.Serializable {
    // 版本号
    private static final long serialVersionUID = 1173984872572414699L;
    // condition队列的头节点
    private transient Node firstWaiter;
    // condition队列的尾结点
    private transient Node lastWaiter;

    // 构造方法
    public ConditionObject() { }

    // 添加新的waiter到wait队列
    private Node addConditionWaiter() {
        // 保存尾结点
        Node t = lastWaiter;
        // If lastWaiter is cancelled, clean out.
        if (t != null && t.waitStatus != Node.CONDITION) { // 尾结点不为空,并且尾结点的状态不为CONDITION
            // 清除状态为CONDITION的结点
            unlinkCancelledWaiters(); 
            // 将最后一个结点重新赋值给t
            t = lastWaiter;
        }
        // 新建一个结点
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        if (t == null) // 尾结点为空
            // 设置condition队列的头节点
            firstWaiter = node;
        else // 尾结点不为空
            // 设置为节点的nextWaiter域为node结点
            t.nextWaiter = node;
        // 更新condition队列的尾结点
        lastWaiter = node;
        return node;
    }

    private void doSignal(Node first) {
        // 循环
        do {
            if ( (firstWaiter = first.nextWaiter) == null) // 该节点的nextWaiter为空
                // 设置尾结点为空
                lastWaiter = null;
            // 设置first结点的nextWaiter域
            first.nextWaiter = null;
        } while (!transferForSignal(first) &&
                    (first = firstWaiter) != null); // 将结点从condition队列转移到sync队列失败并且condition队列中的头节点不为空,一直循环
    }

    private void doSignalAll(Node first) {
        // condition队列的头节点尾结点都设置为空
        lastWaiter = firstWaiter = null;
        // 循环
        do {
            // 获取first结点的nextWaiter域结点
            Node next = first.nextWaiter;
            // 设置first结点的nextWaiter域为空
            first.nextWaiter = null;
            // 将first结点从condition队列转移到sync队列
            transferForSignal(first);
            // 重新设置first
            first = next;
        } while (first != null);
    }

    // 从condition队列中清除状态为CANCEL的结点
    private void unlinkCancelledWaiters() {
        // 保存condition队列头节点
        Node t = firstWaiter;
        Node trail = null;
        while (t != null) { // t不为空
            // 下一个结点
            Node next = t.nextWaiter;
            if (t.waitStatus != Node.CONDITION) { // t结点的状态不为CONDTION状态
                // 设置t节点的nextWaiter域为空
                t.nextWaiter = null;
                if (trail == null) // trail为空
                    // 重新设置condition队列的头节点
                    firstWaiter = next;
                else // trail不为空
                    // 设置trail结点的nextWaiter域为next结点
                    trail.nextWaiter = next;
                if (next == null) // next结点为空
                    // 设置condition队列的尾结点
                    lastWaiter = trail;
            }
            else // t结点的状态为CONDTION状态
                // 设置trail结点
                trail = t;
            // 设置t结点
            t = next;
        }
    }

    // 唤醒一个等待线程。如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁。
    public final void signal() {
        if (!isHeldExclusively()) // 不被当前线程独占,抛出异常
            throw new IllegalMonitorStateException();
        // 保存condition队列头节点
        Node first = firstWaiter;
        if (first != null) // 头节点不为空
            // 唤醒一个等待线程
            doSignal(first);
    }

    // 唤醒所有等待线程。如果所有的线程都在等待此条件,则唤醒所有线程。在从 await 返回之前,每个线程都必须重新获取锁。
    public final void signalAll() {
        if (!isHeldExclusively()) // 不被当前线程独占,抛出异常
            throw new IllegalMonitorStateException();
        // 保存condition队列头节点
        Node first = firstWaiter;
        if (first != null) // 头节点不为空
            // 唤醒所有等待线程
            doSignalAll(first);
    }

    // 等待,当前线程在接到信号之前一直处于等待状态,不响应中断
    public final void awaitUninterruptibly() {
        // 添加一个结点到等待队列
        Node node = addConditionWaiter();
        // 获取释放的状态
        int savedState = fullyRelease(node);
        boolean interrupted = false;
        while (!isOnSyncQueue(node)) { // 
            // 阻塞当前线程
            LockSupport.park(this);
            if (Thread.interrupted()) // 当前线程被中断
                // 设置interrupted状态
                interrupted = true; 
        }
        if (acquireQueued(node, savedState) || interrupted) // 
            selfInterrupt();
    }

    /*
        * For interruptible waits, we need to track whether to throw
        * InterruptedException, if interrupted while blocked on
        * condition, versus reinterrupt current thread, if
        * interrupted while blocked waiting to re-acquire.
        */

    /** Mode meaning to reinterrupt on exit from wait */
    private static final int REINTERRUPT =  1;
    /** Mode meaning to throw InterruptedException on exit from wait */
    private static final int THROW_IE    = -1;

    private int checkInterruptWhileWaiting(Node node) {
        return Thread.interrupted() ?
            (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
            0; 
    }

    private void reportInterruptAfterWait(int interruptMode)
        throws InterruptedException {
        if (interruptMode == THROW_IE)
            throw new InterruptedException();
        else if (interruptMode == REINTERRUPT)
            selfInterrupt();
    }

    // 等待,当前线程在接到信号或被中断之前一直处于等待状态
    public final void await() throws InterruptedException {
        if (Thread.interrupted()) // 当前线程被中断,抛出异常
            throw new InterruptedException();
        // 在wait队列上添加一个结点
        Node node = addConditionWaiter();
        // 
        int savedState = fullyRelease(node);
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            // 阻塞当前线程
            LockSupport.park(this);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) // 检查结点等待时的中断类型
                break;
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }

    // 等待,当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态 
    public final long awaitNanos(long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter();
        int savedState = fullyRelease(node);
        final long deadline = System.nanoTime() + nanosTimeout;
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            if (nanosTimeout <= 0L) {
                transferAfterCancelledWait(node);
                break;
            }
            if (nanosTimeout >= spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
            nanosTimeout = deadline - System.nanoTime();
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
        return deadline - System.nanoTime();
    }

    // 等待,当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态
    public final boolean awaitUntil(Date deadline)
            throws InterruptedException {
        long abstime = deadline.getTime();
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter();
        int savedState = fullyRelease(node);
        boolean timedout = false;
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            if (System.currentTimeMillis() > abstime) {
                timedout = transferAfterCancelledWait(node);
                break;
            }
            LockSupport.parkUntil(this, abstime);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
        return !timedout;
    }

    // 等待,当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。此方法在行为上等效于: awaitNanos(unit.toNanos(time)) > 0
    public final boolean await(long time, TimeUnit unit)
            throws InterruptedException {
        long nanosTimeout = unit.toNanos(time);
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter();
        int savedState = fullyRelease(node);
        final long deadline = System.nanoTime() + nanosTimeout;
        boolean timedout = false;
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            if (nanosTimeout <= 0L) {
                timedout = transferAfterCancelledWait(node);
                break;
            }
            if (nanosTimeout >= spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
            nanosTimeout = deadline - System.nanoTime();
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
        return !timedout;
    }

    //查询是否是同一个AQS
    final boolean isOwnedBy(AbstractQueuedSynchronizer sync) {
        return sync == AbstractQueuedSynchronizer.this;
    }

    //  查询是否有正在等待此条件的任何线程
    protected final boolean hasWaiters() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
            if (w.waitStatus == Node.CONDITION)
                return true;
        }
        return false;
    }

    // 返回正在等待此条件的线程数估计值
    protected final int getWaitQueueLength() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        int n = 0;
        for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
            if (w.waitStatus == Node.CONDITION)
                ++n;
        }
        return n;
    }

    // 返回包含那些可能正在等待此条件的线程集合
    protected final Collection<Thread> getWaitingThreads() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        ArrayList<Thread> list = new ArrayList<Thread>();
        for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
            if (w.waitStatus == Node.CONDITION) {
                Thread t = w.thread;
                if (t != null)
                    list.add(t);
            }
        }
        return list;
    }
}

Unsafe

Unsafe为AQS提供了CAS方法,以及park和unpark方法。

概况

Java无法直接访问底层操作系统,而是通过本地(native)方法来访问。Unsafe提供了硬件级别的原子操作

作用:提供了一些执行低级、不安全的方法。如直接访问系统内内存资源、自主管理内存资源。

image.png

方法

1、

public native long staticFieldOffset(Field paramField);

获取paramField的内存偏移量,这个值对于给定的field是唯一的且固定不变的。

2、

public native int arrayBaseOffset(Class paramClass);
public native int arrayIndexScale(Class paramClass);

第一个方法用于获取数组第一个元素的偏移地址

第二个方法用来获取数组的转换因子(增量地址)

3、

public native long allocateMemory(long paramLong);//分配内存
public native long reallocateMemory(long paramLong1, long paramLong2);//扩充内存
public native void freeMemory(long paramLong);//释放内存

内存操作

UnSafe提供了堆外内存的分配、拷贝、释放、给定地址值操作等相关方法。、

//分配内存, 相当于C++的malloc函数
public native long allocateMemory(long bytes);
//扩充内存
public native long reallocateMemory(long address, long bytes);
//释放内存
public native void freeMemory(long address);
//在给定的内存块中设置值
public native void setMemory(Object o, long offset, long bytes, byte value);
//内存拷贝
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
//获取给定地址值,忽略修饰限定符的访问限制。与此类似操作还有: getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
//为给定地址设置值,忽略修饰限定符的访问限制,与此类似操作还有: putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//获取给定地址的byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果为确定的)
public native byte getByte(long address);
//为给定地址设置byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果才是确定的)
public native void putByte(long address, byte x);

作用

我们一般创造的对象都在JVM的堆中。而用UnSafe提供操作堆外内存的native方法。

使用堆外内存的原因:

1、对垃圾回收STW改善,堆外内存受操作系统管理而不是JVM,所以在垃圾回收时,能保持较小的堆内内存。

2、提升程序IO性能。

CAS相关

public final native boolean compareAndSwapObject(Object o, long offset,  Object expected, Object update);

public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);
  
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);

CAS操作的参数 → 内存位置、预期原值及新值。

执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。

线程调度

UnSafe提供了线程的挂起、恢复、锁机制。

//取消阻塞线程
public native void unpark(Object thread);
//阻塞线程
public native void park(boolean isAbsolute, long time);
//获得对象锁(可重入锁)
@Deprecated
public native void monitorEnter(Object o);
//释放对象锁
@Deprecated
public native void monitorExit(Object o);
//尝试获取对象锁
@Deprecated
public native boolean tryMonitorEnter(Object o);

内存屏障

//内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
//内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
//内存屏障,禁止load、store操作重排序
public native void fullFence();
posted @ 2024-11-07 14:08  ayu0v0  阅读(43)  评论(0)    收藏  举报