java多线程与高并发(1) -- 各种锁

Synchronized

1.基本概念

 - 是非公平可重入锁
 - 字节码层面 是monitor enter  获取锁,monitor exist 释放锁
	普通同步方法:锁是当前实例对象;
	静态同步方法:锁是当前类的class对象;
	同步方法块:synchronized括号里的对象。

2.锁的几种状态

 - 偏向锁
	 1. 只有一个线程使用时,是偏向锁 ,将当前线程id记录在对象的markword中
	 2. 场景:jvm启动时 会多线程竞争锁,如果启动偏向锁需要大量锁撤销,所以直接是到轻量级锁,默认4秒后启动偏向锁。
	 3. xx:BiasedLockingStartupDelay=0  (不延时启动偏向锁的参数)

 - 轻量级锁(自旋)
 	 1. 对象的markword中会有指针记录是哪个线程争抢到了这个锁,这个指针指向线程栈中的lock record。
	 2.  两(多)个线程竞争, 线程A,B  有各自的线程栈, 在栈里生成各自的 lock record  ,如果A先抢到锁,  B则继续自旋,直到A释放锁,B在继续争抢锁。
	 3. 自旋是占用cpu资源的,如果锁时间过长,或者自旋的线程数过多,Cpu资源会被大量占用和消耗

 - 重量级锁
	 1. 会记录在objectMonitor一个字段上
	 2. 重量级锁有等待队列 waitSet,拿不到锁的线程进入等待队列,不需要消耗cpu资源

 - 几种状态的相互转化
	 1. new 对象(普通)
			开启偏向锁-->偏向锁-->轻度竞争,轻量级锁
			未开启偏向锁-->轻量级锁-->有现成超过10次自旋,或者自旋线程数超过cpu核数的一半--->重量级锁
			偏向锁-->重量级锁 :重度竞争或者调用wait()
			
 - 区别
	 1.偏向锁,轻量级锁 :用户空间完成的
	 2.重量级锁 : 需要向内核申请
	 3.用户态和内核态:操作系统  ring0-ring3级 。  jvm是用户态,用synchronized加锁需要向os操作系统申请加锁,即用户态调用内核态  0X80

3.锁的重入

 - 偏向锁&自旋锁: 
	 - 线程栈里的lock record 里有一个指针会记录 前一个锁状态的  备份markword ;
	 - 重入的时候 线程栈里会记录多个指针为空的lock record 然后解锁的时候会一个一个弹出
     - reentrantlock里可重入的是state++

在这里插入图片描述
在这里插入图片描述

ReentrantLock

  • 公平/非公平 可重入锁(自旋锁)

  • 与synchronized相比 更灵活。
    1.tryLock,lockInterruptibly

     - 如果这个线程争抢锁的时候是阻塞状态waiting,其他线程调用该线程的打断方法可以将其打断
    
  • 公平、非公平

     1. 公平锁:new ReentrantLock(true)  在并发环境中,每个线程在获取锁的时候,会先查看锁维护的等待队列,如果为空或者当前线程是等待队列里的第一个,就占有锁,否则就会加入到等待队列中,先进先出的原则,等待队列为双向链表
     2. 非公平:new ReentrantLock(),直接越过等待队列尝试占有锁,否则按照公平锁的方式	  
    
  • 源码:

      - 非公平: lock的时候,直接cas判断state值,如果能把0更新为1就将锁设置为独占的。 否则执行公平锁的逻辑。
      - 公平: acquire(1); 尝试是否能更新state值并设置独占,如果是同一个线程重入锁,则将state值+1.否则将当前线程加到等待 队列中。
      - ReentrantLock implements Lock 并且将Sync 作为他的属性	
      - Sync 继承 AQS . 
      - ReentrantLock.lock()也是调用的Sync.lock()  
      - Sync为抽象类,子类为FairSync 和NonfairSync
      - acquire 获取独占的节点,没有获取到就排队,lock的时候会调用
      - release 释放独占的节点,由一个或者多个非阻塞线程实现该操作,用于unlock
      - unsafe.cas
      - volatile int state
      - addWaiter  cas
      - tryAcquire,tryRelease 由子类reentrantLock实现
    
  • 可重入

    • ReentantLock和Synchronized都是可重入锁

    • 作用:防止死锁

    • sync m1() 调用 sync m2() 他们持有的是同一把锁

    • 例子:

       lock.lock()
       lock.lock()
       lock.unlock()
       lock.unlock()
       两个lock和unlock也会执行成功
       如果两个lock 一个unlock会阻塞住,等待解锁,不会编译和执行报错
       如果多加unlock  会抛IllegalMonitorStateException异常
      
  • Condition

      - lock.newCondition();
      - condition.await();
      - condition.signal();
      - 需要与lock一起使用,生产者消费者模型
    
  • AQS
    在这里插入图片描述

ReadWriteLock

	实现类:ReentrantReadWriteLock
	读写分离 rwl.writeLock().lock();  rwl.readLock().lock();
	写:独占锁,原子性
	读:共享锁,同时进行
	写写、读写 不能共存
	ps:lock.lock()写在try外面,因为lock时候异常,finally 会执行unlock也会异常

	说明:多个线程(包含读写争抢同一把锁),多个读线程可以同时争抢到然后同时执行,多个写线程按顺序执行,多个读线程(同时执行)与写线程按顺序执行

StampedLock

与reentrantlock相同的功能

new StampedLock()
lock.readLock(); //unlockRead
lock.writeLock();  //unlockWrite

乐观读

读多写少的场景适用,防止一直是读线程争抢到锁,导致写线程不执行。
long stamp  = lock.tryOptimisticRead();
if(lock.validate(stamp)){
	lock.readLock();
}

synchronized,readWritelock, stampedLock,Optimistic,对比

synchronized一直表现很平均,甚至是有时候比其他的效率高。
需要根据具体场景进行压测在选择

Condition

LockSupport

park() 如果可以获取许可,直接返回,否则阻塞住counter=0。

unpark() 释放一个许可 counter = 1。 counter值最多为1.

Semapore(1)

AQS,CAS,unsafe类

这些锁的比较,本质上的总结。

练习

1.实现生产消费模式(synchronized,队列,condition...),实现一个锁(读写锁,自旋锁,信号量锁)
2.手写一个读写锁,分别用synchronized 和cas方式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值