享学堂-架构师网课笔记-线程-L2

偏向锁的实现

CAS

CAS(compare and swap) 对比和交换,对比两个值,如果相同则交换,如果不同则返回false,是原子性的操作,操作系统提供用户态的方法,性能比较高。

公平锁和非公平锁的实现

公平锁和非公平锁,其中里面的值如果为false,则非公平锁,如果是true,则为公平锁。公平锁和非公平锁在Java源码的实现的并没有实现通过系统调用mutex(用户态切换到内核态调用),所以性能是比较高的,而在使用synchronized时,如果有多个线程竞争的话,会转化为主重量级锁。而此时则会产生系统调用,使性能下降。
private static Lock lock=new ReentrantLock(false);
而这两种锁的实现主要区别在他们加锁的方式上:

/**
     * 公平锁的实现
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * 公平锁的实现
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            //判断是否已经加锁,状态为0 则未被加锁,可以加锁,执行CAS,然后线程ID放入队列
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果加锁失败则判断是否被自己加过锁,否则进入队列等待
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

非公平锁的实现

 	/**
     * 非公平锁的实现
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
    	 *直接尝试加锁,如果加锁失败则进入acquire
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
	/**
	*当加锁失败,会进入队列查看自己是否为head,如果为head则自旋一次
	*/
   public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

在这里插入图片描述
总结:公平锁和非公平锁的主要区别在于一开始是否直接尝试加锁,而这点也是公平锁和非公平锁的区别,设计初衷是为了提高性能。

synchronized

偏向锁的膨胀过程

1,对象的加锁方式

JVM是通过修改对象的对象头里面的标识来进行加锁,在class文件中会有这样一个对象头记录文件,其中无锁的分为两种,无锁可偏向(101)和无锁不可偏向(001),同时有锁已偏向也是101,但会记录持有锁的线程ID。所以无锁肯定是001。而轻量级锁一般来说是00。
在这里插入图片描述
在这里插入图片描述
所以我们可以研究不同的场景下加锁方式,对象头会发生怎样的变化来搞清楚synchronized的使用方式。

重入锁和偏向锁

重入锁指的是一个线程对一个对象进行加锁,再加锁的过程中再对这个对象进行加锁。而偏向锁是对这个对象进行加锁,加锁后释放然后再次进行加锁。

   A a = new A();
        //偏向锁的加锁过程
        synchronized (a) {

        }
        synchronized (a){
            
        }
        //重入锁的加锁过程
        synchronized (a){
            
            synchronized (a){
                
            }
        }

重入锁的加锁过程,简单来说就是修改对象头+记录线程ID。当一个线程尝试对对象进行加锁时,在线程操作数栈中创建一个叫lockrecord的数据结构,包括displaced word 用于记录锁对象的对象头。对对象进行首次加锁时首先lr中产生101,然后把自己的线程ID放到对象1的对象头。此时对象头标记就是 t1ID+eporch+age+101,表示线程1 的偏向锁。当锁被释放后,再次加锁,只需要对比下是否是t1的线程ID然后如果相等直接再次加锁,不用进行CAS,此时性能是最高的。
在这里插入图片描述

轻量锁

当一个线程加锁后,是释放,第二个线程进行加锁时,就会升级成为轻量级锁。加锁的过程,首先displaced word 会产生一个有锁不可偏向001,然后对象1的对象头markwork会创建一个指针指向了lr的地址,而Obj ref 也会产生一个指针指向对象1。
在这里插入图片描述
当线程2糟蹋完对象1后,就会把自身的001放到对象1的Markwork中去,表示对象1不可偏向。所以轻量锁和偏向锁还是有区别的。
在这里插入图片描述

轻量锁加锁(本来已经轻量)

当线程t3要对线程进行加锁时首先会在CPU的内存中产生一个无锁的Markwork001,此时要对CPU内存中的001和线程3内存中的Markwork进行对比。如果相等,则进行跟上面一样的操作,Obj ref 把指针指向线程1,Markwork指向同步块,并且把001变成00。
在这里插入图片描述
而如果发生CAS失败,说明在t3产生001的过程中有另外的线程t4已经将对象加锁,变成000,产生资源竞争,所以会发生膨胀。

批量重偏向和批量撤销

在线程1中,反复对一个对象进行加锁,但是没有竞争,当撤销超过20次以后,jvm 会把这些对象加锁时重新偏向t2。而如果超过40次,jvm觉得设计有问题,所有的对象都撤销偏向锁。

wait notify

在这里插入图片描述
wait() 是对象的方法,在对象头实现的。在对象头的前64位会指向一个指针,指向ObjectMonitor。在ObjectMonitor 这个数据结构里面存储了一个WaitSet,EntryList,ownerThread,其中ownerThread记录的是当前线程的持有者,EnteyList 存储的是 阻塞的线程状态是blocked,WaitSet 是底层是waiting状态的队列。而当EntryList的队列阻塞完成后会进入到阻塞队列中去,而不是直接执行。

wati 和sleep的区别

相同:都是使线程进入休眠状态
不同: wait 在阻塞阶段会释放锁,sleep不会。wait是作用在对象上的,而sleep是作用在线程上的。wait 可以通过notify唤醒,而sleep只能通过打断唤醒。wait 必须通过synchronized关键字一起使用,而一个对象没有获取锁直接wait会异常,而sleep不会。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值