所谓显式锁和隐式锁,主要指的就是 synchronized 关键字和 ReentrantLock 类。
下面具体聊一聊二者之间的区别:
1 底层不同
synchronized 是java中的关键字,是JVM层面的锁。
ReentrantLock 是是JDK5以后出现的具体的类。使用lock是调用对应的API。
我们通过Javap命令来查看调用二者的汇编指令:
可以看出 synchronized 是底层是通过monitorenter进行加锁,通过monitorexit来退出锁的。
ReentrantLock 对象是通过调用对应的API方法来获取锁和释放锁的。
2 使用方式不同
正如文章的标题那样,显式锁和隐式锁最大的不同就是在使用的时候,程序员要不要手动写代码去获取锁和释放锁的操作。
在使用 synchronized 关键字的时候,我们使用者根本不用写其他的代码,然后程序就能够获取锁和释放锁了。那是因为当synchronized 代码块执行完成之后,系统会自动的让程序释放占用的锁。 如下:
public class Demo9 { public static void main(String[] args) { //线程不安全 //解决方案2 同步方法 Runnable runnable = new Ticket(); new Thread(runnable).start(); new Thread(runnable).start(); new Thread(runnable).start(); } static class Ticket implements Runnable{ //总票数 private int count = 10; @Override public void run() { while (true) { boolean flag = sale(); if(!flag){ break; } } } public synchronized boolean sale(){ if (count > 0) { //卖票 System.out.println("正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count); return true; } return false; } } }
在使用lock的时候,我们使用者需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。
手动获取锁方法:
lock.lock();
手动释放锁:
lock.unlock();
举例如下:
public class Demo10 { public static void main(String[] args) { Runnable run = new Ticket(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } static class Ticket implements Runnable{ //总票数 private int count = 10; private Lock l = new ReentrantLock(); @Override public void run() { while (true) { l.lock(); if (count > 0) { //卖票 System.out.println("正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count); }else { break; } l.unlock(); } } } }
3 是否可以中断
synchronized 是不可中断的。除非抛出异常或者正常运行完成
lock可以中断的。中断方式:
调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断
4 是否是公平锁
synchronized 是非公平锁
lock创建时可以传入一个Boolean值来设置是公平锁还是非公平锁:
true:公平锁
false:非公平锁
5 性能
在java1.6之前synchronized性能较低,因为其需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还要多,相比之下lock的性能更高一些。
然而到了java1.6之后对锁进行了很多的优化,进而出现轻量级锁,偏向锁,锁消除,适应性自旋锁导致synchronized的性能也不差,并且由于其在语义上很清晰所以也被官方更多的支持。
6 使用锁的方式
lock 获取锁与释放锁的过程,都需要程序员手动的控制 Lock用的是乐观锁方式。
乐观锁:每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
synchronized托管给jvm执行 原始采用的是CPU悲观锁机制
悲观锁:线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。