功能类比
- 实现了雷同synchronized(obj)的类似同步功能,但跟synchronized(obj)不一样,也就是说reentracntlockojb.lock跟synchronized(reentracntlockojb)不互斥。
- ReentrantLock lock中的lock锁的对象是自己,相当于:synchronized(lock){ }。
- 锁多少次就unlock多少次,否则锁不能释放,或者报IllegalMonitorStateException异常。
lock/unlock
public class TestThreadLock1 implements Runnable{
private ReentrantLock lock = new ReentrantLock();
public void run() {
lock.lock();
{
//逻辑代码
}
lock.unlock();
//类似
/*synchronized (lock) {
//逻辑代码
}*/
}
}
注意:
- unlock必须保持跟lock一样的数量,多了就报:
-
Exception in thread "main" java.lang.IllegalMonitorStateException at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151) at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261) at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
- unlock最后在finally中写,原因你懂得。
isHeldByCurrentThread
查询当前线程是否保持此锁,这样就不会报错了,因为unlock的时候你也不知道线程是否还保持次锁,所以需要判断一下。
ReentrantLock l = new ReentrantLock();
if(l.isHeldByCurrentThread()){
l.unlock();
}
lockInterruptibly
可以被interrupt的lock
Thread1中run方法内调用lock1.lock();这个方法的时候,如果无法获取lock1的锁,那么久无限等待的。
如果在别的Thread2内有Thread1对象,且想终端Thread1,调用:Thread1.Interrupt()是无法中断的。
如果把lock1.lock()替换为可中断锁lock1.lockInterruptibly(); 那么调用该线程的interrupt方法是可以中断的。
解决死锁代码:
/**
* 测试死锁中断
* 1. 无法使用interrupt对synchronized对资源的等待,也没法打断ReentrantLock.lock的等待
* - 可以使用interrupt对ReentrantLock.lockInterruptibly对资源的等待,见TestThreadLock3
* 2. 可中断锁lockInterruptibly
* 3. 如果使用了可中断锁,中断后对没有锁的lock就不能unlock,否则保持,需要使用isHeldByCurrentThread判断,线程是否被当前线程给终端
*/
public class TestThreadLock3 implements Runnable{
private static Logger log = Logger.getLogger(TestThreadLock3.class);
private ReentrantLock t1 = null;
private ReentrantLock t2 = null;
public TestThreadLock3(ReentrantLock t1,ReentrantLock t2){
this.t2 = t2;
this.t1 = t1;
}
public static void main(String[] args) throws Exception {
ReentrantLock t1 = new ReentrantLock();
ReentrantLock t2 = new ReentrantLock();
Thread th1 = new Thread(new TestThreadLock3(t1,t2));
Thread th2 = new Thread(new TestThreadLock3(t2,t1));
th1.start();
th2.start();
//需要打断点进行
Thread.sleep(1000l * 3);
th1.interrupt();
}
public void run() {
try {
this.t1.lockInterruptibly();
log.info("get t1 monitor ...");
Thread.sleep(1000l * 1);
log.info("get t2 monitor ...");
t2.lockInterruptibly();
} catch (Exception e) {
e.printStackTrace();
} finally{
if(t1.isHeldByCurrentThread()){
this.t1.unlock();
}
if(t2.isHeldByCurrentThread()){
this.t2.unlock();
}
log.info(Thread.currentThread().getId() + "退出!");
}
}
}
tryLock(…)
锁申请等待限时,这个当然就不会产生死锁了。
- lock是无限制等待,直到获得锁,比较执着
- lockInterruptibly是无限制等,直到获取锁或者自己所在的线程被interrupt
- tryLock() 是试着获取锁,获取到锁返回true,否则直接返回false,不会等待
- tryLock(3 s) 是试着获取锁,获取到了直接返回true,没获取到等个3s,如果3s后没获取到返回false
示例代码:
/**
* 锁申请等待限时
* 1. tryLock() 如果获取到锁则继续执行,如果锁被其他线程持有,则立即返回 false ,也就是不会使当前线程等待,所以不会产生死锁。
* 2. tryLock(long timeout, TimeUtil unit)指定时长内获取到锁则继续执行,如果等待指定时长后还没有获取到锁则返回false。
* 3. 当然如果无法获取锁就需要在业务逻辑做出处理
*/
public class TestThreadLock4 implements Runnable{
private static Logger log = Logger.getLogger(TestThreadLock3.class);
private ReentrantLock t1 = null;
public TestThreadLock4(ReentrantLock t1){
this.t1 = t1;
}
public static void main(String[] args) throws Exception {
ReentrantLock t = new ReentrantLock();
new Thread(new TestThreadLock4(t)).start();
Thread.sleep(1000l * 50);
}
public void run() {
try {
Thread.sleep(500l);
if(!t1.tryLock()){
//对没有获取到锁子的处理
log.info("t1 没有获取到锁");
//return; //正常逻辑没有获取到所就直接返回,这里测试就注释了
}
if(!t1.tryLock(3,TimeUnit.SECONDS)){
//对没有获取到锁子的处理
log.info("t1 没有在指定时间内获取到锁");
return; //正常逻辑没有获取到所就直接返回,这里测试就注释了
}
//获取到锁的处理
log.info("get lock success ...");
} catch (Exception e) {
log.error("",e);
} finally{
if(this.t1.isHeldByCurrentThread()){
t1.unlock();
}
}
}
}
公平锁
对比非公平锁,如果等待的线程是有先来后到就是公平锁,否则就是不公平。
刚放弃锁的线程,再次获取锁,相当于最后来的。
图示:
示例代码:
/**
* 测试公平锁
* 1. 公平锁就是按照时间先后顺序,使先等待的线程先得到锁,而且,公平锁不会产生饥饿锁,也就是只要排队等待,最终能等待到获取锁的机会。使用重入锁(默认是非公平锁)创建公平锁:
* 2. 默认是:new ReentrantLock()使用的是NonfairSync,即非公平锁
* public ReentrantLock() {
* sync = new NonfairSync();
* }
* 3. 公平锁创建:new ReentrantLock(true)
* public ReentrantLock(boolean fair) {
* sync = fair ? new FairSync() : new NonfairSync();
* }
* 4.公平锁日志:可以发现,t1和t2交替获取到锁
* 2018-07-20 16:39:37,876 [t1] INFO [com.haiwei.lock.TestThreadLock3] - i = 1
2018-07-20 16:39:37,876 [t2] INFO [com.haiwei.lock.TestThreadLock3] - i = 1
2018-07-20 16:39:37,985 [t2] INFO [com.haiwei.lock.TestThreadLock3] - i = 2
2018-07-20 16:39:38,094 [t1] INFO [com.haiwei.lock.TestThreadLock3] - i = 2
2018-07-20 16:39:38,204 [t2] INFO [com.haiwei.lock.TestThreadLock3] - i = 3
2018-07-20 16:39:38,313 [t1] INFO [com.haiwei.lock.TestThreadLock3] - i = 3
2018-07-20 16:39:38,422 [t2] INFO [com.haiwei.lock.TestThreadLock3] - i = 4
2018-07-20 16:39:38,526 [t1] INFO [com.haiwei.lock.TestThreadLock3] - i = 4
2018-07-20 16:39:38,637 [t2] INFO [com.haiwei.lock.TestThreadLock3] - i = 5
2018-07-20 16:39:38,752 [t1] INFO [com.haiwei.lock.TestThreadLock3] - i = 5
*5.非公平锁日志:(顺序没法保证)
*2018-07-20 16:41:32,689 [t1] INFO [com.haiwei.lock.TestThreadLock3] - i = 1
2018-07-20 16:41:32,689 [t2] INFO [com.haiwei.lock.TestThreadLock3] - i = 1
2018-07-20 16:41:32,689 [t2] INFO [com.haiwei.lock.TestThreadLock3] - i = 2
2018-07-20 16:41:32,689 [t2] INFO [com.haiwei.lock.TestThreadLock3] - i = 3
2018-07-20 16:41:32,689 [t1] INFO [com.haiwei.lock.TestThreadLock3] - i = 2
2018-07-20 16:41:32,689 [t1] INFO [com.haiwei.lock.TestThreadLock3] - i = 3
2018-07-20 16:41:32,689 [t1] INFO [com.haiwei.lock.TestThreadLock3] - i = 4
2018-07-20 16:41:32,689 [t1] INFO [com.haiwei.lock.TestThreadLock3] - i = 5
2018-07-20 16:41:32,689 [t2] INFO [com.haiwei.lock.TestThreadLock3] - i = 4
2018-07-20 16:41:32,689 [t2] INFO [com.haiwei.lock.TestThreadLock3] - i = 5
*/
public class TestThreadLock5 implements Runnable{
private static Logger log = Logger.getLogger(TestThreadLock3.class);
private ReentrantLock t1 = null;
public TestThreadLock5(ReentrantLock t1){
this.t1 = t1;
}
public static void main(String[] args) throws Exception {
//创建公平锁true;非公平锁false
ReentrantLock t = new ReentrantLock(true);
new Thread(new TestThreadLock5(t),"t1").start();
new Thread(new TestThreadLock5(t),"t2").start();
Thread.sleep(100000l);
}
public void run() {
try {
int i = 0 ;
while(i++ < 5){
log.info(" i = " + i);
this.t1.lock();
t1.unlock();
}
} catch (Exception e) {
log.error("",e);
} finally{
if(this.t1.isHeldByCurrentThread()){
t1.unlock();
}
}
}
}
await/ signal
实现等待和唤醒,还记得生产者消费者模式不。
记住:await(condition接口方法),不是wait(是Object的方法)
/**
* 测试ReentrantLock的等待和唤醒
* - 类似synchronized(obj)中的obj.wait和obj.notify,需要锁住后再调用
* ReentrantLock中操作等待是Conditond对象,方法为:await和signal
* 1. ReentrantLock 实现了Lock接口,可以通过该接口提供的newCondition()方法创建Condition对象
* Condition condition = lock.newCondition();
* 2.Condition接口:
* public interface Condition {
void await() throws InterruptedException; // 类似于Object.wait()
void awaitUninterruptibly(); // 与await()相同,但不会再等待过程中响应中断
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal(); // 类似于Obejct.notify()
void signalAll();
}
* 3. 注意:类似
* 等待和唤醒需要同一个Condition方可,因为lock.newCondition()两次就会产生两个Condition对象
* 4. 记住了,一定是await(condition接口方法),不是wait(是Object的方法)
* 5. 日志:
* 2018-07-20 17:11:05,017 [t1] INFO [com.haiwei.lock.TestThreadLock3] - get t1 lock ..
2018-07-20 17:11:08,022 [main] INFO [com.haiwei.lock.TestThreadLock3] - signal all ...
2018-07-20 17:11:08,022 [t1] INFO [com.haiwei.lock.TestThreadLock3] - wait end ..
*/
public class TestThreadLock6 implements Runnable{
private static Logger log = Logger.getLogger(TestThreadLock3.class);
private ReentrantLock lock = null;
private Condition condition = null;
public TestThreadLock6(ReentrantLock t1,Condition c1){
this.lock = t1;
this.condition = c1;
}
public static void main(String[] args) throws Exception {
ReentrantLock lock = new ReentrantLock(true);
Condition condition = lock.newCondition();
new Thread(new TestThreadLock6(lock,condition),"t1").start();
Thread.sleep(1000l * 3);
lock.lock();
condition.signalAll();
lock.unlock();
log.info("signal all ...");
Thread.sleep(10000l * 5);
}
public void run() {
try {
log.info("get t1 lock ..");
this.lock.lock();
this.condition.await();
//记住了,一定是await(condition接口方法),不是wait(是Object的方法)
log.info("wait end ..");
} catch (Exception e) {
log.error("", e);
} finally{
if(this.lock.isHeldByCurrentThread()){
lock.unlock();
}
}
}
}