掌握之并发编程-3.锁

发布时间:2020-06-24 11:01:03 作者:学习Lr
来源:网络 阅读:206

掌握高并发、高可用架构

第二课 并发编程

从本课开始学习并发编程的内容。主要介绍并发编程的基础知识、锁、内存模型、线程池、各种并发容器的使用。

第三节 锁

并发编程 并发基础 AQS Synchronized Lock

这小节咱们来学习并发编程中锁的知识。主要包括关键字synchronized、各种LockAQS的原理、以及各自的应用。

synchronized

可以修饰方法或者代码块

表示多个线程访问该方法或者代码块时要进行排队,串行的执行该方法或者代码块

执行效率低,但是它是并发编程容器的基础

分类 具体分类 被锁的对象 示例代码 说明
方法 实例方法 类的实例对象 synchronized void methodA() {};<br />void methodB() {};<br />synchronized void methodC() {}; 线程调用了同步方法,<br />别的线程可以调用非同步方法,<br />对于其他同步方法,必须该方法在执行完成后才能调用<br />不影响静态方法的调用(包括同步,非同步)
静态方法 类对象 static synchronized void methodA() {};<br />static void methodB() {};<br />static synchronized void methodC() {}; 线程调用了同步方法,<br />别的线程可以调用非同步方法,<br />对于其他同步方法,必须该方法在执行完成后才能调用<br />不影响对象方法的调用(包括同步,非同步)
代码块 实例对象 类的实例对象 synchronized(this) {} 同上
class对象 类对象 synchronized(SynchronizedTest.class) {} 同上
任意实例对象 实例对象Object Object lock = new Object();<br />synchronized(lock) {} 只影响锁住的对象,而不影响类和类的实例对象
synchronized的实现机制

JAVA对象头Monitor是实现synchronized的基础。

  1. JAVA对象头,对于Hotspot虚拟机的对象头主要包含两部分数据:Mark Word(标记字段)、Klass Pointer(类型指针)

    • Mark Word,用于存储对象自身的运行时数据,如哈希码(Hash Code)、GC分代年龄、锁状态标识、线程持有的锁、偏向线程ID、偏向时间戳等
    • Klass Pointer,是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例
  2. Monitor,是一种同步机制,即同一时刻只允许一个线程进入Monitor的临界区,从而达到互斥的效果。synchronized的对象锁,其指针指向的是一个Monitor对象的起始地址。每个对象实例都有一个monitor。由C++实现,其数据结构如下。

    ```C++
    ObjectMonitor() {
    _count = 0;
    _owner = NULL;
    _waitSet = NULL;
    _waitSetLock = 0;
    _EntryList = NULL;
    }

    
    
    其中,**_owner**指向持有ObjectMonitor对象的线程。当多个线程同时访问一段同步代码时,会把线程存放在锁的对象的**_EntryList**中。当某个线程获得对象的Monitor时,就会把*_owner*的值设置为当前线程,同时*_count*加1。如果线程调用**wait()**方法,就会释放当前持有的Monitor,*_owner*置为null,*_count*减1,并将该线程放入**_waitSet**中。当然,如果持有monitor的线程正常执行完毕,也会释放monitor,*_owner*置为null,*_count*减1。

对于加在代码块上的synchronized,其字节码是:一次monitorenter、两次monitorexit(含有一次编译器自动生成的异常处理的monitorexit);

对于加在方法上的synchronized,其字节码是:标识方法为ACC_SYNCHRONIZED

新特性

synchronized是一个重量级锁,相较Lock,比较笨重,不高效。在JDK1.6中,其实现过程引入了大量的优化,如自旋锁自适应自旋锁锁消除锁粗化偏向锁轻量级锁等技术来减少锁的开销。

锁的等级

依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态

Lock

Lock是一个接口,有以下方法。

public interface Lock {
    void lock();
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    void lockInterruptibly();
    Condition newCondition();
}

这里说下方法void lockInterruptibly()一个线程获得锁之后是不可以被interrupt()方法中断的,是不能中断正在执行中的线程的,只能中断阻塞过程中的线程lockInterruptibly方法允许当线程等待获取锁的过程中由其他线程来中断等待。

Lock 和 synchronized的区别与相同点

区别:

相同点:

ReentrantLock

可重入锁,是Lock接口的唯一实现类。

可重入锁:是指如果一个线程获得了一个对象的锁,那么它不需要再获取该对象的锁而可以直接执行方法。也就是锁的分配机制是基于线程来分配的,而不是基于方法调用的分配。

可中断锁:可以响应中断的锁。只有lockInterruptibly()方法的锁是可中断锁,lock()还是不可中断的

公平锁:指尽量以请求锁的顺序来获取锁。比如有多个线程在等待一个锁,当锁被其他线程释放时,最先请求锁的线程会获得该锁

非公平锁:无法保证锁的获取是按照请求锁的顺序进行的。可能导致某个或者一些线程永远获取不到锁

对于ReentrantLock,默认是非公平锁,但可指定为公平锁。

ReentrantLock lock = new ReentrantLock(true)

ReentrantLock中定义了两个内部类,一个是NotFairSync,一个是FairSync。当构造器参数为true时,表示创建公平锁,参数为false或者无参时,表示非公平锁。

ReadWriteLock
public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}

一个用来获取读锁,一个用来获取写锁。

ReentrantReadWriteLock

可重入读写锁,实现了ReadWriteLock接口。

多个线程同时进行读操作时,会使多个线程交替进行,从而提高读操作的效率。但是,如果有线程占用读锁,此时其他要获取写锁的话,就必须等待读锁释放后才可执行;如果有线程占用写锁,此时其他线程不管是要获取读锁或者写锁的话,都必须等待写锁释放。

Lock的实现原理

ReentrantLockFairSyncNotFairSync都继承了AbstractQueuedSynchronizer,并且真正lock()unlock()的实现过程都是在AQS中。

首先,AQS的数据结构是:一个表示锁状态的变量volatile int state,取值范围是 0 无锁、1 有锁;一个用于存储等待获取锁的线程的双向链表transient volatile Node headtransient volatile Node tail

其次,加锁流程NotFairSync.lock()是:

  1. 通过CAS去尝试获取锁:判断当前state是0的话表示无锁,然后把当前线程设置为独占执行线程,再修改state为1表示有锁
    掌握之并发编程-3.锁

  2. 如果第一步获取锁失败,那么执行acquire(1)(这是AQS的方法)

掌握之并发编程-3.锁

主要是三个方法:

掌握之并发编程-3.锁

掌握之并发编程-3.锁
下面借两张图(<https://yq.aliyun.com/articles/640868>)

掌握之并发编程-3.锁cdn.com/71e8b71038243dfaf21ebcf6f9fcc5fbaa659b08.png">

获取锁的流程:

掌握之并发编程-3.锁

然后,解锁的流程是调用NotFairSync.release(),主要是对重入数量的调整。每次释放锁都只会对数量减1,直到state为0时表示锁释放完成。
掌握之并发编程-3.锁

Lock和synchronized的选择

根据CAS的特性,建议在低锁冲突的情况下使用Lock

在JDK1.6后,官方对synchronized做了大量的优化(偏向锁、自旋、轻量级锁等),因此在非必要的情况下,建议都使用synchronized做同步操作

推荐阅读:
  1. Python全栈开发之并发编程
  2. 3.协议与端口

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

synchronized aqs cas

上一篇:17. Gradle编译其他应用代码流程(五) - 设置Task过程

下一篇:seaborn是什么?

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》