Java线程不安全问题有三种解决方案,所谓显式锁和隐式锁即Lock和synchronized的区别。
一、构成不同
Sync:Java中的关键字,是由JVM来维护的,是JVM层面的锁。
Sync 底层是通过 monitorenter 进行加锁(底层是通过 monitor 对象来完成的,其中的wait/notify等方法也 是依赖于 monitor 对象的。只有在同步代码块或者同步方法中才可以调用wait/notify等方法。因为只有在同步代码块或者是同步方法中,JVM才会调用 monitory 对象);通过 monitorexit 来退出锁。
Lock:是JDK5以后才出现的具体的类。使用 Lock 是调用对应的API,是API层面的锁。
而 Lock 是通过调用对应的API方法来获取锁和释放锁。
二、使用方式不同
Sync是隐式锁,Lock是显示锁。
所谓的显示和隐式就是在使用的时候,使用者要不要手动写代码去获取锁和释放锁的操作。
->在使用sync关键字的时候,程序能够自动获取锁和释放锁。因为当sync代码块执行完成之后,系统会自动让程序释放占用的锁。Sync是由系统维护的,如果非逻辑问题的话,是不会出现死锁的。
->Lock:手动获取和释放,不然可能导致出现死锁的现象。
Demo:
三、等待是否可中断
Sync是不可中断的,除非抛出异常或者正常运行完成。
Lock可以中断的。
中断方式:
1、调用设置超时方法tryLock(long timeout ,timeUnit unit)
2、调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断
四、加锁的时候是否可以公平
Sync:非公平锁。即排队抢锁。
Lock:两者都可以的。默认是非公平锁。在其构造方法的时候可以传入Boolean值。
true:公平锁。即Lock lo = new ReentrantLock(true)
false:非公平锁
五、锁绑定多个条件来condition
Sync:要么随机唤醒一个线程,要么是唤醒所有等待的线程。
Lock:用来实现分组唤醒需要唤醒的线程,可以精确的唤醒,而不是像sync那样,不能精确唤醒线程。
六、性能比较
Sync是托管给JVM执行的。在JDK5中,前者因为需要调用接口操作,可能加锁等消耗时间更长。但在JDK6后,sync进行很多优化,有适应自旋、锁消除、锁粗化、轻量级锁、偏向锁等,性能也不比lock差。
Lock是java写的控制锁的代码。对象性能更高点。
之前的sync使用悲观锁的机制,即线程独占锁,其它线程只能依靠阻塞来等待线程释放的锁,而线程阻塞时会引起线程的上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。
而Lock使用乐观锁的方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁的实现方式是CAS机制(Compare And Swap),调用的是CPU提供的底层指令。