并发笔记

AQS(AbstactQuenuedSynchronier)

具体的实现类有:ReentranLock
Semaphore countDownLatch
RentranWritelck
AQS全称为AbstractQueuedSynchronizer,它提供了一个FIFO队列,可以看成是一个用来实现同步锁以及其他涉及到同步功能的核心组件,常见的有:ReentrantLock、CountDownLatch等
AQS的两种功能

  1. 从使用层面来说,AQS的功能分为两种:独占和共享
    独占锁,每次只能有一个线程持有锁,比如前面给大家演示的 ReentrantLock 就是以独占方式实现的互斥锁;
    共享锁,允许多个线程同时获取锁,并发访问共享资源,比如 ReentrantReadWriteLock;
  2. AQS的内部实现
    AQS的实现依赖内部的同步队列,也就是 FIFO 的双向队列,如果当前线程竞争锁失败,那么AQS会把当前线程以及等待状态信息构造成一个 Node 加入到同步队列中,同时再阻塞该线程。当获取锁的线程释放锁以后,会从队列中唤醒一个阻塞的节点(线程)。

CAS(CompareAndSetState) :比较交换:

解释:对于m++操作,如果要实现安全的,会对m进行加锁,才能保证,
cas就不需要加锁copareAndsetState(0,1):需要传入两个值,要去判断是不是m的值,如果是0,我就给他变成1,如果不是0,就说明别的线程进去了,就不做任何操作证,用cas时用死循环去判断他的值是不是0,这就是去征用锁的意思但是没有加锁,在不停循环叫自旋。

CAS:必须是原子操作,现在的cpu都支持cas(乐观锁)修改的是内存地址的偏移量

在AbstractQueuedsynchronier内部是储存了一个Atomicinteger +一个双向链表(Clh),如果判断Atomicinteger不为0,就会插入到双向链表中,
aqs释放锁的时候需要判断当前的排他锁的线程,和当前线程是不是一样的,一样的才能释放(isHeldExclusively)方法中

public class JLock implements Lock {
    
    private Sync sync = new Sync();
    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public void unlock() {
        sync.release(1);

    }

    @Override
    public Condition newCondition() {
        return null;
    }

    private class Sync extends AbstractQueuedSynchronizer{
        @Override
        protected boolean tryAcquire(int arg) {
            assert arg == 1; //arg!=1时会终止
            if(compareAndSetState(0,1)){ // 是1时,代表线程拥有了这把锁
                setExclusiveOwnerThread(Thread.currentThread()); //互斥锁
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            assert arg ==1;
            if(!isHeldExclusively()) throw  new IllegalArgumentException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
            
            
        }
        
        @Override
        protected boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();  
        }
    }
}

互斥锁;

共享锁:(读锁)如果是一个读锁进来的话,他不改变里面的内容,是可以的如果是一个写锁进来就不行了

volatile : m在主内中,线程a做++,同时b也去操作是看不到的,这时 加一个volatile就对两块线程可见了 图

volatile:禁止指令重排。
指令重排:在创建对象的时候,1,分配内存,2,成员变量初始化,3.构造函数初始化,4,把地址指向引用对象。
但是在内存中创建的时候可能不是按顺序执行的,可能拿到一个没有初始化完成的引用,如单例模式的双重加锁,静态成员变量就要被volatile修饰,多个线程在抢占资源的时候,可能就会拿到没有初始化完全的锁,就会有问题

HostPot :jvm源码

synchrnoied实现

了解synchrnoied底层所需要的知识

  1. cas
  2. cas底层实现
  3. 对象的内存布局
  4. markword的细节
    底层:
  5. 锁的升级的过程
  6. c++
  7. 锁消除
  8. 锁粗化

1 CAS:

内存中的m=0,线程a读出来进行++操作,然后放回去,和内存中的值进行比较,如果中的值时0,就直接加1,如果不是1,就又把现在m的值读出来做++操作在和内存的值比较,直到操作成功为止这叫 cas(compare and swap)比较交换 这是自旋锁
ABA问题:线程a对m=0进行操作,线程b把m改为3,线程c把m又改回0了
解决:加一个版本号,每经历一个线程版本++

  1. AtomicInteger m = new AtomicInteger:

cas最底层实现是调用 lock cmpxchg指令实现(汇编),但是这条语句不是原子的,这就是在cmpxchg前面要加lock的原因,我在进行操作的时候不允许打断我

对象的内存布局:

T {
int m = 8;
}
new T(); 的底层实现分为四部分:;
1.makeword 8个字节 2,klass pointer 3 .m 4个字节 4.padding

  1. klass pointer 是一个指针 指向T.class 知道是那个对象的实例 默认4个 不压缩是8个
  2. padding 对齐(对于64位的虚拟机 padding )1+2+3+4必须能被8整除
    object o = new object()默认为16字节 8+4+4
    makeword是干什么用的呢:
    所谓的锁就是修改了makework的信息
    makeword是一个8字节的头信息:记录了锁信息,hashcode,gc信息

锁的升级

用户态和内核态:以前用户是可以直接调用硬件信息的,容易崩掉,在中间加了一层内核态,只能通过我才能去访问硬件
锁升级:以前加锁是需要通过操作系统的调度才能拿到锁,所以是一个重量级的锁,现在优化以后,就直接去底层拿锁,省掉了访问内核态的一个过程,比如cas
锁优化

volatile:防止指令重排,线程可见性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值