Java多线程之synchronized与Lock
2017-06-10 22:44
183 查看
首先我们需要先理解一下什么是共享受限资源。
我们来看下这段代码,在多线程环境下,一个线程可能在另外一个线程刚刚执行完第一行++currentEvenValue的时候调用next方法,此时就会造成读取的currentEvenValue变量的值不正确,造成程序错误甚至崩溃,当然这个可能是一个小概率事件,但是根据墨菲定律,可能会出现的就一定会出现。大家可以自己测试下,为了更快的发现,我们可以使用Thread.yield()来快速发现失败。
为了解决这个冲突,Java内置了synchronized关键字来解决资源冲突。在Java中所有的对象都含有单一的锁(监视器),当在对象上调用任意声明为synchronized的方法的时候,,此对象都会被加锁,这时此对象的别的synchronized方法就只有等前一个方法再执行完毕并释放锁之后才能被调用。简单点来说,就是某个对象的所有synchronized方法共享同一个锁。
注意:在使用synchronized时要将域设置为private,否则synchronized无法阻止其他任务直接访问从而产生冲突。
一个任务可以多次获得对象的锁,如果一个方法在同一个对象上调用了另外的synchronized方法就会发生这种情况。jvm负责跟踪对象被枷锁的次数,在任务第一次给对象加锁的时候,计数为1,当这个任务再次获得锁时,计数会递增,每当任务离开一个synchronized方法计数会递减,当计数为0时锁被完全释放。每个类也有一个锁,作为类的Class对象的一部分,所以synchronized static方法可以再类的范围内防止对static数据的并发访问。
说了这么多,贴下对上述问题的解决代码:
针对上一问题,我们也可以使用显式的Lock对象来加锁。
在这里需要注意的需要注意的是如果需要继续调用lock方法,必须放置在带有unlock()的try-finally子句中,还有就是return必须在try子句中出现,以避免unlock不会过早发生。
看到这里我们不禁要问,synchronized与Lock的作用是一样的,那么他们俩到底有什么区别呢?
ReentranLock允许我们尝试获取但最终未获取到锁:
还可以尝试着去获取锁,但是会在指定的时间单位后失败:
显式的Lock对象在加锁与释放锁方面,相对于内建的synchronized锁来说,赋予了我们更细粒度的控制,我们根据需要来选择具体使用。有一点需要注意的是:Lock对象加锁所造成的阻塞可以被中断,而synchronized不行。
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不行。
相关文章推荐
- Java多线程之synchronized与Lock
- Java多线程之synchronized与Lock
- Java多线程之synchronized与Lock
- java多线程(3)---synchronized、Lock
- Java多线程之synchronized与Lock
- Java多线程之synchronized和Lock
- Java多线程之synchronized与Lock
- Java多线程之synchronized与Lock
- Java多线程之synchronized与Lock
- Java多线程简析——Synchronized(同步锁)、Lock以及线程池
- Java多线程之synchronized与Lock
- java多线程:synchronized和lock比较浅析
- Java多线程的synchronized和Lock
- Java线程锁,以及volatile、synchronized、Lock关键字
- Java并发编程:synchronized和Lock
- ReentrantLock和synchronized比较
- Java中的ReentrantLock和synchronized两种锁定机制的对比
- 浅谈Java多线程(synchronized实现可见性)
- Java:使用synchronized和Lock对象获取对象锁
- synchronized 和 ReentrantLock 该用哪个?