(借鉴自刘望舒的Android进阶之光)
这个很好理解,有些资源是不安全的,如果多个线程同时访问会引起结果的错误,所以我们需要给他加一个同步锁。即synchronized关键字,没有人不熟悉他的,但是还有一个重入锁就比较冷门了。
Lock l = new ReentrantLock();
l.lock();
try {
...
} finally {
l.unlock();
}
这个锁保证了同一时刻,这一个代码块(称之为临界区)只有一个线程可以进入。这里finally中解锁是很有必要的,否则如果一旦发生异常,另外所有的线程都阻塞住了。
可是当进入临界区的时候,发现只有子安一个条件满足之后,这个线程才能执行,又该怎么办。这个时候就可以用一个条件对象(别名条件变量)来管理那些已经获得了一个锁但是却不能做有用工作的线程。
一个例子说明条件对象的重要性,比如大家都在转账,这个操作必须是同步的吧。但是一旦他钱不够了怎么转给别人?这个时候难道另外的线程跟着一起死锁?所以需要把这个线程转入这个条件的等待集并置为阻塞状态,知道另一个线程调用了同一个条件的signalAll()(这是把这个条件的所有阻塞线程都开启,并且让他们在外面排队,重新竞争)为止。
public void transfer(int from, int to, int amount) throws InterruptedException{
mLock.lock();
try {
while (accounts[from] < amount) {
condition.await();
}
accounts[from] = accounts[from] - amount;
accounts[to] = accounts[to] + amount;
condition.signalAll();
} finally {
mLock.unlock();
}
}
while就是当钱不够的时候,把这个线程让条件对象来管理。下面的condition.signalAll();就是把条件对象管理的线程全部都激活,让他们重新排队。如果没有线程可以进入这个临界区,那就死锁了。
全部代码
class pay {
private double[] accounts;
private Lock mLock;
private Condition condition;
public pay(int n, double money) {
accounts = new double[n];
mLock = new ReentrantLock();
condition = mLock.newCondition();
for (int i = 0; i < accounts.length; i ++) {
accounts[i] = money;
}
}
public void transfer(int from, int to, int amount) throws InterruptedException{
mLock.lock();
try {
while (accounts[from] < amount) {
condition.await();
}
accounts[from] = accounts[from] - amount;
accounts[to] = accounts[to] + amount;
condition.signalAll();
} finally {
mLock.unlock();
}
}
}
不过用synchronized锁住一个方法是更方便的选择
public synchronized void method(){
...
}
用synchronized关键字来改写上面的transfer方法
public synchronized void transfer(int from, int to, int amount) throws InterruptedException {
while (accounts[from] < amount) {
wait();
}
accounts[from] = accounts[from] - amount;
accounts[to] = accounts[to] + amount;
notifyAll();
}
简洁很多。使用synchronized,你必须了解到每一个对象有一个内部所,并且这个锁有一个内部条件。wait().notifyAll()用法和之前的类似。
synchronized也可以通过使用同步代码块来获得这个锁。
synchronized(obj) {
}
再用这个方法改写一下
public synchronized void transfer(int from, int to, int amount) throws InterruptedException {
synchronized((new Object())) {
accounts[from] = accounts[from] - amount;
accounts[to] = accounts[to] + amount;
}
}
这种方法不推荐用,因为仅仅只能锁住括号里的对象,代码块是锁不了的!一般同步实现最好用comcurrent并发包下的类。