我们继续来探讨同步机制的话题;
上一篇文章说到了使用ReentrantLock的lock()来获取锁,但有时候我们可能有这样的需求,我们先尝试着去获取锁,但等了几秒实在拿不到锁就放弃获取锁而转做其它的事情,这个需求是很常见的, 所幸的是,ReentrantLock提供了tryLock方法来帮助我们达到这样的目的;
//演示锁的tryLock方法
public static void testTrylock() throws InterruptedException{
final ReentrantLock lock = new ReentrantLock();
//开启一个线程A,让它先拿到锁,再休眠几秒
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
System.out.println("线程A已拿到锁,并且休眠8秒");
TimeUnit.MILLISECONDS.sleep(8000);
System.out.println("线程A执行完毕!");
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
}).start();
//开启一个线程B
new Thread(new Runnable() {
@Override
public void run() {
boolean holdLock = false;
try {
TimeUnit.MILLISECONDS.sleep(10);
System.out.println("线程B尝试获取锁");
// lock.lock();
// System.out.println("线程B拿到锁,准备载入资源...");
// 如果我们写上面的代码,很明显,lock.lock()这里必须等待8秒等到线程A执行完毕才能拿到锁,后面所有的执行都会被阻塞
// ,但有时候我们想先等几秒看看,实在拿不到锁就做其它事,这时我们可以这么写,如下所示:
//尝试获取锁,3秒后拿不到锁就做其它事
holdLock = lock.tryLock(3000, TimeUnit.MILLISECONDS);
if(holdLock){
System.out.println("拿到锁,准备载入资源...");
}else{
System.out.println("拿不到锁...放弃载入资源...");
}
} catch(Exception e){
e.printStackTrace();
} finally {
if(holdLock){
lock.unlock();
}
}
}
}).start();
}
输出**********************************************************************
线程A已拿到锁,并且休眠8秒
线程B尝试获取锁
拿不到锁...放弃载入资源...
线程A执行完毕!
***************************************************************************
tryLock这个比较简单,不再多说,最后,我们来证明一下锁机制锁住的是当前对象;
//演示锁机制是作用与对象之上的!
public static void testObjSyn(){
//证论:如果对象A有方法b,c,d b,c方法均加锁,d为普通方法
//如果线程T持有A对象的锁且进入方法b,则其余线程无法进入方法c但可以进入普通方法d
//节省代码,直接写内部类了!
class A{
//声明一个对象,专门用作锁,在实际运用中,你不要直接写个Object,而是要持有一个有意义的对象
//Object synObj = new Object(); //*****代码1处******
public synchronized void b(){
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A.b()");
}
public void c(){
// synchronized (synObj) { //*****代码2处******
synchronized (this) {
System.out.println("A.c()");
}
}
public void d(){
System.out.println("A.d()");
}
}
final A aObj = new A();
new Thread(new Runnable() {
public void run() {aObj.b();}
}).start();
new Thread(new Runnable() {
public void run() {aObj.c();}
}).start();
new Thread(new Runnable() {
public void run() {aObj.d();}
}).start();
}
运行后,我们看到输出结果是,先输出A.d() ; 等两秒后再一并输出A.b() A.c();
结论:第一个线程调用b()时,锁定的是当前的对象也就是aObj,第二线程调用c()时,该对象锁继续被第一个线程持有,所以无法获取到锁,但d()是普通方法,无需获取锁所以能调用;
解决办法:如果b()和c()两个方法无任何业务关联性,根本不需要在同一把锁中执行,则可以声明一把新锁;(业务关联性是指:假设b()方法是下单,c()是减库存,则两个方法必须一起执行,c()的执行依赖于b()的执行结果)
将代码1处于代码2处的注释去掉,在A类中声明一把新锁synObj,再运行试试看;