Java 重入锁与条件对象

Java 重入锁与条件对象

重入锁 ReentrantLock 是 Java 1.5 引入的,重入的意思是指可以重复获取锁,即拿到锁的对象可以再次拿一次锁,而不必先释放上一个锁。

ReentrantLock 实现了 Lock 接口。用 ReentrantLock 保护代码块的结构如下:

    private void dosomethingLock() {
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
            dosomething();
        } finally {
            lock.unlock();
        }
    }

这一结构保证了任何时候只有一个线程进入临界区,临界区就是在同一个时刻只有一个任务可以访问的代码区。unlock 操作放在 finally 可以保证锁一定可以被释放。

如果进入临界区时,发现在某一个条件满足之后才能执行,这时可以使用一个条件对象 Condition 来管理那些已经获得了锁但是却不能做有用工作的线程。

下面用一个转账的例子说明重入锁和条件对象。

public class Alipay {
    private double[] accounts;
    private Lock alipayLock;

    public Alipay(int n, double money) {
        accounts = new double[n];
        alipayLock = new ReentrantLock();
        for (double a : accounts) {
            a = money;
        }
    }
}

以上代码定义了一个 Alipay 用来管理 n 个账户,其中有一个重入锁 alipayLock。

接着定义一个转账方法 transfer。

    public void transfer(int from, int to, int amount) {
        alipayLock.lock();
        try {
            while (accounts[from] < amount) {
                condition.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            alipayLock.unlock();
        }
    }

如果转账的金额大于当前账户的余额,那么条件对象 condition 就 await 等待,直到其他账户先转入给它足够的余额。这里的 condition 是通过 ReentrantLock 的 newCondition 方法构造的。

调用了 condition 的 await 后就会放弃锁,同时等待,进入该 condition 的等待集。一旦有其他线程调用了同一个条件对象的 signalAll 方法后,就会重新激活因为这个条件等待的所有的线程。

    public void transfer(int from, int to, int amount) {
        alipayLock.lock();
        try {
            while (accounts[from] < amount) {
                condition.await();
            }
            accounts[from] -= amount;
            accounts[to] += amount;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            alipayLock.unlock();
        }
    }

调用 signalAll 方法时并不是立即激活了一个等待线程,而是解除了等待线程的阻塞,让它们继续往下执行。还有一个方法是 signal,它是解除某一个线程的阻塞,如果该线程仍然不能运行,则再次被阻塞,如果没有其他线程再次调用 signal,那么系统就死锁了。

最终的代码如下,账户 1 依靠账户 0 的转账使得账户 1 有足够的余额转给账户 2。

public class Alipay {
    private double[] accounts;
    private Lock alipayLock;
    private Condition condition;

    public Alipay(int n, double money) {
        accounts = new double[n];
        alipayLock = new ReentrantLock();
        condition = alipayLock.newCondition();
        for (int i = 0; i < n; ++i) {
            accounts[i] = money;
        }
    }

    public void transfer(int from, int to, int amount) {
        alipayLock.lock();
        try {
            while (accounts[from] < amount) {
                System.out.println("no enough, await: " + Thread.currentThread().getName());
                condition.await();
            }
            accounts[from] -= amount;
            accounts[to] += amount;
            condition.signalAll();
            System.out.println("transfer ok, signal all:" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            alipayLock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Alipay alipay = new Alipay(3, 20);
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("transfer 1 to 2, amount 30");
                alipay.transfer(1, 2, 30);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("transfer 0 to 1, amount 10");
                alipay.transfer(0, 1, 10);
            }
        }).start();
    }
}

以上代码输出结果如下:

transfer 1 to 2, amount 30
no enough, await: Thread-0
transfer 0 to 1, amount 10
transfer ok, signal all:Thread-1
transfer ok, signal all:Thread-0

可以看出账户 0 的转账调用了 signalAll,这时线程 Thread-0 收到信号继续执行,当它发现余额足够转出就跳出循环,往下执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值