一个简单的ReentrantLock的例子, 情景是几个朋友吃饭, 可是美味的汤只有一锅, 勺子只有一个. 这样一来, 难免就会有你抢我争的情况了. 但是, 如果有更美味的其他食物, 当然可以先转头去找其他的了. synchronized是无法做到这点的.
Lunch类, 包括勺子(ReentrantLock)和"舀"的动作, 当这帮朋友想要"舀"的时候, 就只能一个人动手, 其他人乖乖等着, 或者被叫去干其他事情
package concurrent.lunch;
import java.util.concurrent.locks.ReentrantLock;
public class Lunch {
private ReentrantLock scoop = new ReentrantLock();
public boolean dip() {
try {
scoop.lockInterruptibly();
} catch (InterruptedException e) {
Logger.log("Some one call me for better food ^^ ");
return false;
}
Logger.log("hah, I got the scoop");
try {
// suppose we need 5s to dip the soup
try {
Thread.sleep(5000);
} catch (InterruptedException i) {
Logger.log("someone rob my scoop, 55~~~ ");
return false;
}
Logger.log("I got delicious food ");
} finally {
scoop.unlock();
}
return true;
}
}
Buddy类, 嘴馋的家伙, 抢着要"舀"汤, 不过如果抢不到, 也可以干别的
package concurrent.lunch;
public class Buddy extends Thread {
private final Lunch lunch;
public Buddy(Lunch lunch, String name) {
this.lunch = lunch;
this.setName(name);
}
public void run() {
while (!lunch.dip()) {
Logger.log("I will wait for a while to dip, see if any other better...");
try {
Thread.sleep(100);
} catch (InterruptedException ignore) {}
}
}
}
Party类, 宴会开始了, 每个人都想去抢那个勺子, 抢不到的又实在等不耐烦的话就只好暴力解决(interrupt)
package concurrent.lunch;
public class Party {
public static void main(String[] args) throws Exception {
// here we have to share the Lunch instance
Lunch lunch = new Lunch();
// here we MUST share the Lunch instance
Buddy Tityz= new Buddy(lunch, "Tityz");
Buddy Michael = new Buddy(lunch, "Michael");
Buddy Yutting= new Buddy(lunch, "Yutting");
Tityz.start();
Thread.sleep(100); //make sure Tityz got it first
Michael.start();
Yutting.start();
Thread.sleep(1000);
// why still hanging? rob him
Tityz.interrupt();
// ask michael to other food
Michael.interrupt();
}
}
Logger:
package concurrent.lunch;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Logger {
private static final SimpleDateFormat df = new SimpleDateFormat("hh:mm:ss");
public static void log(String msg) {
System.out.println(df.format(new Date()) + " "
+ Thread.currentThread().getName() + ":\t" +msg );
}
}
好了, 开始宴会吧.
02:42:24 Tityz: hah, I got the scoop
02:42:25 Tityz: someone rob my scoop, 55~~~
02:42:25 Michael: Some one call me for better food ^^
02:42:25 Yutting: hah, I got the scoop
02:42:25 Michael: I will wait for a while to dip, see if any other better...
02:42:25 Tityz: I will wait for a while to dip, see if any other better...
02:42:30 Yutting: I got delicious food
02:42:30 Tityz: hah, I got the scoop
02:42:35 Tityz: I got delicious food
02:42:35 Michael: hah, I got the scoop
02:42:40 Michael: I got delicious food
结果显而易见, 首先是Tityz拿到勺子, 但是因为"舀"比较花时间, 其他人都只能等. 不过一段时间(1s)过去了, 其他人忍不住了, "抢"了他的勺子, 而Michael则被叫去做其他事情了. 得渔利者Yutting也. Yutting搞定后, Tityz再次抢到勺子(这里的次序是随机的), 这次没人打断他了, Michael则最后才喝到汤.
例子完了, 但是我们应该考虑到问题是, 这个锁定, 到底锁定的对象是什么? ReentrantLock.lock()没有参数, 不想synchronized(xx)可以指定被锁定的对象. 那么我们只能假设ReentrantLock.lock()维护了内部的对象. 显然, 如果我们new了好几个ReentrantLock实例并且每个线程分别持有一个, 那么这些线程最终获取的锁定的对象就不是同一个. 这就是上面例子的Party里共用一个ReentrantLock的原因.
当然, 共用的形式不一定就是通过直接传递ReentrantLock对象给某个线程, 也可以是在线程执行的方法去共用ReentrantLock, 自己发挥想象力吧
同时也说一下上面代码的缺点, 留意一下上面代码Lunch.dip()的方法签名, public boolean dip(), 是带有返回值的, 这样的做法可谓喜忧参半, 好的一面是, 如果失败了, 调用该方法的线程有机会进行其他事情的处理, 不利的一面是调用该方法的线程被逼要使用不断尝试的方式来处理, 增加了代码复杂度.
我们有一种解决上述问题的做法, 就是让调用的线程等待, 直到条件满足为止. 可以参考java.util.concurrent.locks.Condition类的使用例子.
ReentrantLock 到底锁定了哪个对象了?
最新推荐文章于 2023-12-04 22:44:27 发布