1.ReentrantLock的源码简单解读
/*
* ReentrantLock是轻量级锁
* 非公平锁即不检查是否为阻塞队列头成员,直接让到达的程序进行锁定
* 公平锁会进行队列检查确保不抢占
* ----
* 如果没有竞争状态,并发只是交替执行,如果线程竞争,则会调用阻塞队列来控制线程
* ----
* 面对抢占性的其它线程,源码并没有对同时占到的线程进行回滚,但没有出现问题应该是其它底层控制对其进行了回滚
* ----
* reentrantLock是允许同个线程的重入的,源码有currentThread == newThread?的判断
* ----
* 对于阻塞队列,在被使用时才进行初始化,head初始化为threadNull作为第一个节点,第一个线程节点Node入队,为队列链表的第二个节点(head一直作为threadNull)
* 完成入队操作,判断node.ahead==head?不是,直接park()。如果是,自旋一次,对lock进行acquire操作,若失败则修改上一个节点waitState为-1,再自旋一次
* 如果还是没有得到lock,则进行park操作
* 关于修改上一个节点waitState代表自身阻塞状态
* 一是若阻塞了不能操作,若先修改如果park失败但自身waitState修改了会异常。二是解锁过程中,是调用头节点waitState判断是否对第二个节点进行unPark
* 所以自身的waitState是代表下一个,当head偏移到最后一个(将最后一个等待unPark并threadNull),下次直接判断waitState==0,不进行唤醒操作即可
* ----
* 总结:是否竞争 --> 是否排队(初始化判断、队头判断) --> {自旋+park()+CAS},在队头判断里,注意h!=t&&((s=h.next)==null||s.thread!=Thread.currentThread())
* ----
* 在lock代码有打断处理代码,但代码设计为不执行。因为与lockInterruptibly(允许打断)都调用parkAndCheckInterrupt(),代码达到统一
* 如果是打断的话,是自我打断,使得被自我打断的线程跳到catch异常处理中去,但是并不会干扰原先进程的运行,所以如果要访问共享资源的话还是要进行锁的排队,不过也可以不访问共享资源,破除死锁
* */
2.对ReentrantLock的测试
普通lock的测试:
import java.util.concurrent.locks.ReentrantLock;
public class rlockThread implements Runnable {
ReentrantLock lock;
int serverTime = 0;
public rlockThread(boolean fair) {
// 无参默认非公平锁,true是公平锁
lock = new ReentrantLock(fair);
}
@Override
public void run() {
try {
lock.lock();
serverTime += 10;
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " have server util: " + serverTime);
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + "Thread wrong!");
} finally {
lock.unlock();
}
}
}
结果:(注:由于线程的机器启动上有一定的时差,所以可能会让先start()的Thread反而晚启动。)
公平锁:
非公平锁:
可打断的lock:
import java.util.concurrent.locks.ReentrantLock;
public class rIntpLock implements Runnable {
ReentrantLock lock;
int serverTime = 0;
public rIntpLock(boolean fair) {
// 无参默认非公平锁,true是公平锁
lock = new ReentrantLock(fair);
}
@Override
public void run() {
try {
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + " get the lock!");
Thread.sleep(1000);
serverTime += 10;
System.out.println(Thread.currentThread().getName() + " have server util: " + serverTime);
} catch (InterruptedException e1) {
// 打断异常处理
System.out.println(Thread.currentThread().getName() + " is jump to excetion!");
try {
// lock.lockInterruptibly();
Thread.sleep(3000);
serverTime += 10;
System.out.println(Thread.currentThread().getName() + " have server util: " + serverTime);
} catch (InterruptedException e) {
System.out.println(e);
}
} finally {
lock.unlock();
}
}
}
结果:
3.await和signal的应用
手动阻塞调度:利用reentrantlock的condition的await()和signal()可以进行定向的阻塞队列管理,await即阻塞、释放锁,signal即唤醒线程,占有锁。(signalAll()在主控制上较为常用)
示例:(模拟资源的增加与获取)
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class RAwaitAndSignal implements Runnable {
private ReentrantLock lock = new ReentrantLock();
private int source = 0;
private Condition full = lock.newCondition();
private Condition empty = lock.newCondition();
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
offer();
Thread.sleep(1000);
require();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void offer() throws InterruptedException {
lock.lock();
try {
if (source >= 10)
full.await();
source++;
System.out.println(Thread.currentThread().getName() + ":+1=" + source);
empty.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void require() throws InterruptedException {
lock.lock();
try {
if (source == 0)
empty.await();
source--;
System.out.println(Thread.currentThread().getName() + ":-1=" + source);
full.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}