默认情况下使用的是非公平锁,锁的本质其实就是去阻塞一个线程
自定义阻塞的方式:
wait()方法 一定要搭配synchronized关键字一起使用的
sleep()方法 睡眠的时间无法确定
park()方法
while(true){…} //设置条件state=1死循环,unlock解锁是state变为0
CAS方法参数:当前对象,对象内部修改的变量的偏移量,原来的值,修改后的值
ReentrantLock上锁时,会自动将里面的state属性加1,上一把锁+1一次,释放锁的时候再去将state属性减1;
公平锁实现时,会先去判断当前队列中是有线程在排队,如果有,则通过短路的&&操作阻止本线程去将state从0改为1,也就是本线程此时拿不到锁,即使有线程已经释放了资源,之后用当前线程去创建一个Node节点对象将其放到队列中,设置前后指针,并将当前线程阻塞。如果此时有多个线程同时要进入队列,那么谁会先获得成功呢?其实是基于CAS失败重试实现的,同时将当前线程的节点对象和队列的尾结点tail相连,谁先CAS成功,谁去连接尾结点,对于竞争失败的节点线程,会重复的去竞争,源码调用了一个enq方法,里面就是重复竞争尾结点,CAS+失败重试的思想
节点对象中的waitstate属性有两个值:0和-1,如果waitsate的值为0,代表该节点的线程释放锁之后不用去解锁下一个对象,如果为-1,代表用完之后需要去解锁下一个线程节点。所以当一个节点入队之后会将前一个节点的waitstate的属性修改为-1,即一个线程何时被唤醒取决于前一个线程节点,这样设计是为了解决多线程下的线程中断问题。
队列的head属性指向的并不是一个实际的线程,所以如果某一个线程入队寻找队列的最后一个元素是,发现是head节点,就证明当前线程就是第一个线程,是不需要去排队的,本线程就直接再去尝试获取锁而不是被阻塞,自旋。一旦获取到锁,就将节点的thread属性置为空,head指针指向当前节点,获取不到锁就修改前一个节点的waitstate值为-1。实际获取锁时还会先判断当前线程节点是不是head节点
如果线程进入队列时,队列的head属性==tail属性,该线程不需要去排队,直接去竞争锁就行了。还会判断头结点不等于尾结点情况下,头结点的下一个节点是不是空,如果是空,本线程还要去排队,这种情况时考虑多线程并发的情况,其他线程节点修改节点指针时不是原子操作,可能出现线程节点之前前一个节点head完成,但是head节点还没来得及指向线程节点,也就是尾结点的情况,这时头结点线程不等于尾结点线程,并且头结点的下一个节点还是空。还会判断正在排队的是不是自己这个线程,如果是,不用去排队,直接去竞争锁
该类队列内部基于AQS的双向链表实现的队列,该队列还有head和tail属性。每个节点有当前线程对象属性,等待状态属性,实际上公平锁判断时,就是去判断是否有node对象在排队
非公平锁的加锁不会考虑是不是需要排队的,和队列是没有关系的,一起抢,谁抢到是谁的,但是RentrantLock里面是是需要去排队的,会判断state是不是0,是0直接去抢,抢2次还没有成功就会去排队,不同于完全的非公平锁。公平锁时state为0会先去判断需不需要排队