关于并发编程:Java-并发编程AQS-的互斥锁与共享锁

42次阅读

共计 1353 个字符,预计需要花费 4 分钟才能阅读完成。

咱们晓得古代机器处理器简直都是多核多线程的,引入多核多线程机制是为了尽可能晋升机器整体解决性能。然而多核多线程也会带来很多并发问题,其中很重要的一个问题是数据竞争,数据竞争即多个线程同时访问共享数据而导致了数据抵触(不正确)。数据竞争如果没解决好则意味着整个业务逻辑可能出错,所以在高并发环境中咱们要特地留神这点。

数据竞争产生的条件

存在数据竞争的场景必须满足以下几个条件:

  • 多个线程对某个共享数据进行拜访。
  • 这些线程同时地进行拜访。
  • 拜访即是读或写数据操作。
  • 至多有一个线程是执行写数据操作。

数据竞争例子

为更好了解数据竞争问题,上面咱们举一个数据竞争的例子。上面两张图,下面的是不存在数据竞争时正确的后果。刚开始内存中 i =0,线程一读取后将 i 加 5。批改完后线程二才读取内存中的 i 并将其加 6,最终 i =11。而上面的状况则不同,线程二在线程一还没批改完就读取内存中 i,此时导致最终的后果为 i =6。

同步与锁

既然多个线程并发执行常常会波及数据竞争问题,那么咱们该如何解决这个问题呢?答案就是引入同步机制,通过同步机制来管制共享数据的拜访,就可能解决数据竞争问题。实现同步机制能够通过锁来实现,所以 AQS 框架也形象出了锁的获取操作和开释操作。而且还提供了包含独占锁和共享锁两种模式,这样对于下层的各种同步器的实现就不便很多了

独占锁

独占锁是指该锁一次只能由一个线程持有,其它线程则无奈取得,除非已持有锁的线程开释了该锁。一个线程只有在胜利获取锁后能力持续往下执行,当来到竞争区域时则开释锁,开释的锁供其余行将进入数据竞争区域的线程获取。

获取独占锁和开释独占锁别离对应 acquire 办法和 release 办法。获取独占锁的次要逻辑为:先尝试获取锁,胜利则往下执行,否则把线程放到期待队列中并可能将线程挂起。开释独占锁的次要逻辑为:唤醒期待队列中一个或多个线程去尝试获取锁。在 AQS 中能够用以下伪代码示意独占锁的获取与开释

获取独占锁的伪代码

if(尝试获取锁失败){
    创立 Node
    应用 CAS 把 Node 增加到队列尾部
 while(true){if( 尝试获取锁 && Node 的前驱节点为头节点){
           把以后节点设置为头
           跳出循环
        }else{
           应用 CAS 形式批改 Node 前驱节点的 waitStatus 为 Singal
 if(批改胜利){挂起以后线程}
        }
    }
}

开释独占锁的伪代码

if(尝试开释锁胜利){唤醒后续节点蕴含的线程}

共享锁

获取共享锁和开释共享锁别离对应 acquireShared 办法和 releaseShared 办法。获取共享锁的次要逻辑为:先尝试获取锁,胜利则往下执行,否则把线程放到期待队列中并可能将线程挂起。开释共享锁的次要逻辑为:唤醒期待队列中一个或多个线程去尝试获取锁。在 AQS 中能够用以下伪代码示意共享锁的获取与开释。

公众号:码农架构

Java 并发编程

  • Java 并发编程:Java 序列化的工作机制
  • Java 并发编程:并发中死锁的造成条件及解决
  • Java 并发编程:过程、线程、并行与并发
  • Java 并发编程:工作执行器 Executor 接口
  • Java 并发编程:AQS 的互斥锁与共享锁
  • Java 并发编程:什么是 JDK 内置并发框架 AQS
  • Java 并发编程:AQS 的原子性如何保障
  • Java 并发编程:如何避免在线程阻塞与唤醒时死锁
  • Java 并发编程:多线程如何实现阻塞与唤醒

正文完
 0