天天看点

线程同步,同步锁,重入锁、条件对象

(借鉴自刘望舒的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并发包下的类。