相同点:两者都是同步锁、可重入锁,只有获得该锁的使用权后,线程才能使用否则将会进行等待,并且由于是可重入锁,JVM会允许同一个线程重复的获取同一种锁。
不同点:synchronized是一个关键字,依赖于JVM底层实现,在给代码块或实例方法加锁时,使用this对象作为锁,给静态方法加锁时,使用当前对象的class类作为锁,所以在底层是通过对象内部的monitor监视器实现,线程通过执行monitorenter/monitorexit指令尝试获取monitor的所有权,当monitor被占用时就会处于锁定状态。获取锁是通过线程抢占的形式,并且只支持非公平锁,在方法执行结束自动释放锁。
ReentrantLock实现了Lock接口,在获取锁、释放锁时必须手动使用lock()、unlock()方法,并且支持公平锁和非公平锁,默认情况下使用非公平锁,相比于synchronized,可以通过tryLock()尝试获取锁,性能更安全,并且提供了大量更高级的并发功能,能大大简化多线程程序的编写。
两者都可以暂停线程的执行。但最主要的区别在于:sleep方法没有释放锁,而wait方法释放了锁。
wait()通常被用于线程间的交互/通信,当调用wait()方法后,线程不会自动苏醒,需要别的线程调用同一个对象的notify()或者notifyAll()方法。或者wait(long timeout)超时后线程自动苏醒。
sleep()通常用于线程暂停/休眠,sleep()方法执行完成后,线程会自动苏醒。
悲观锁 (Pessimistic Locking),具有强烈的独占和排他特性。它指的是对数据被外界修改持保守态度。因此,在整个执行过程中,将处于锁定状态。所以,悲观锁是一种悲观思想,它总认为最坏的情况可能会出现,它认为数据很可能会被其他人所修改,所以悲观锁在持有数据的时候总会把资源 或者 数据 锁住,这样其他线程想要请求这个资源的时候就会阻塞,直到等到悲观锁把资源释放为止。Java中的Synchronized和ReentrantLock是一种悲观锁思想的实现,因为Synchronized和ReentrantLock不管是否持有资源,它都会尝试去加锁。
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。乐观锁的思想与悲观锁的思想相反,它总认为资源和数据不会被别人所修改,所以读取不会上锁,但是乐观锁在进行写入操作的时候会判断当前数据是否被修改过。Java中的StampedLock和AtomicInteger是一种乐观锁思想的实现。
synchronized是一个关键字,依赖于JVM底层实现,在给代码块或实例方法加锁时,使用this对象作为锁,给静态方法加锁时,使用当前对象的class类作为锁,所以在底层是通过对象内部的monitor监视器实现,线程通过执行monitorenter/monitorexit指令尝试获取monitor的所有权,当monitor被占用时就会处于锁定状态。
0
,则该线程进入monitor,然后将进入数设置为1
,该线程即为monitor的所有者,代表持有锁;0
,再重新尝试获取monitor的所有权。在JVM底层的锁有三种锁:偏向锁、轻量级锁、重量级锁
偏向锁是在单线程的情况下自动获得的锁,当有多个线程不是在并发条件下执行,会升级成轻量级锁,如果有并发执行再升级成重量级锁。
在升级成轻量级锁的过程中,JVM虚拟机会在当前线程的栈帧中建立一个Lock Record锁记录,存放当前对象锁的Mark Word的拷贝,拷贝成功后,会将当前对象所的Mark Word中的ptr_to_lock_record更新为指向Lock Record的指针,并将Lock Record里的owner指针指向到对象的Mark Word。如果更新成功,代表升级成轻量级锁,并将对象Mark Word的lock标志位设置为00.如果失败,检查对象的Mark Word是否已经指向当前线程的栈帧,如果指向,则继续执行,否则,标志位变成10,升级成重量级锁,其他等待线程阻塞,当前线程自旋尝试获取锁。