Java多线程之synchronized与Lock

首先我们需要先理解一下什么是共享受限资源。

private int currentEvenValue = 0;
public int next() {
    ++currentEvenValue;//++I表示先自增在赋值,I++表示先赋值在自增
    //Thread.yield();
    ++currentEvenValue;
    return currentEvenValue;
}
我们来看下这段代码,在多线程环境下,一个线程可能在另外一个线程刚刚执行完第一行++currentEvenValue的时候调用next方法,此时就会造成读取的currentEvenValue变量的值不正确,造成程序错误甚至崩溃,当然这个可能是一个小概率事件,但是根据墨菲定律,可能会出现的就一定会出现。大家可以自己测试下,为了更快的发现,我们可以使用Thread.yield()来快速发现失败。

为了解决这个冲突,Java内置了synchronized关键字来解决资源冲突。在Java中所有的对象都含有单一的锁(监视器),当在对象上调用任意声明为synchronized的方法的时候,,此对象都会被加锁,这时此对象的别的synchronized方法就只有等前一个方法再执行完毕并释放锁之后才能被调用。简单点来说,就是某个对象的所有synchronized方法共享同一个锁。

注意:在使用synchronized时要将域设置为private,否则synchronized无法阻止其他任务直接访问从而产生冲突。

一个任务可以多次获得对象的锁,如果一个方法在同一个对象上调用了另外的synchronized方法就会发生这种情况。jvm负责跟踪对象被枷锁的次数,在任务第一次给对象加锁的时候,计数为1,当这个任务再次获得锁时,计数会递增,每当任务离开一个synchronized方法计数会递减,当计数为0时锁被完全释放。每个类也有一个锁,作为类的Class对象的一部分,所以synchronized static方法可以再类的范围内防止对static数据的并发访问。

说了这么多,贴下对上述问题的解决代码:

private int currentEvenValue = 0;
public synchronized int next() {
    ++currentEvenValue;//++I表示先自增在赋值,I++表示先赋值在自增
    Thread.yield();
    ++currentEvenValue;
    return currentEvenValue;
}
针对上一问题,我们也可以使用显式的Lock对象来加锁。

private Lock lock = new ReentrantLock();
public int anotherNext(){
    lock.lock();//枷锁
    try {
        ++currentEvenValue;
        Thread.yield();
        ++currentEvenValue;
        return currentEvenValue;
    }finally{
        lock.unlock();//释放锁
    }
在这里需要注意的需要注意的是如果需要继续调用lock方法,必须放置在带有unlock()的try-finally子句中,还有就是return必须在try子句中出现,以避免unlock不会过早发生。

看到这里我们不禁要问,synchronized与Lock的作用是一样的,那么他们俩到底有什么区别呢?

ReentranLock允许我们尝试获取但最终未获取到锁:

private ReentrantLock lock = new ReentrantLock();
public void unTimed(){
    boolean captured = lock.tryLock();
    try{
        System.out.println("tryLock():"+captured);
    } finally {
        if(captured){
            lock.unlock();
        }
    }
}
还可以尝试着去获取锁,但是会在指定的时间单位后失败:

public void timed(){
    boolean captured = false;
    try {
        captured = lock.tryLock(2, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    try{
        System.out.println("tryLock(2, TimeUnit.SECONDS):"+captured);
    } finally {
        if(captured){
            lock.unlock();
        }
    }
}
显式的Lock对象在加锁与释放锁方面,相对于内建的synchronized锁来说,赋予了我们更细粒度的控制,我们根据需要来选择具体使用。有一点需要注意的是:Lock对象加锁所造成的阻塞可以被中断,而synchronized不行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值