Java锁——synchronized、ReentrantLock和ReentrantReadWriteLock

synchronized关键值

  • 线程在进入synchronized同步代码块之前会自动获得锁,退出时无论是正常退出还是抛出异常退出,都会自动释放锁。
  • synchronized修饰的同步块对同一条线程来说是可重入的
  • synchronized修饰的同步块的锁被线程持有时,后面的其他线程会被阻塞。

重入锁ReentrantLock

可以实现非块结构的互斥同步。

与synchronized的比较

  1. 性能上
    从JDK6,ReentrantLock与synchronized性能上基本持平。所以,目前性能不再是选择使用两种锁的因素了。
  2. 功能上
    • ReentrantLock锁上,等待的线程可以选择放弃等待,可定时,可轮询,可中断获取锁;
    • ReentrantLock锁提供公平锁和非公平锁,默认下是非公平的(synchronized是非公平的)。公平锁指线程按照请求的顺序获得锁,非公平锁允许插队的发生。
      线程的挂起和恢复都需要系统开销,加上线程恢复和真正执行之间存在的延迟。那么当锁被释放时,如果刚好有一个线程到来,直接分配给它,这样的不公平策略会减少线程切换的开销,所以一般情况下非公平锁性能要优于公平锁。在锁持有时间较长,或者请求锁的间隔大的时候,可以使用公平锁。
    • ReentrantLock锁可以绑定多个条件。synchronized同步块需要配合锁对象的wait()、notify()和notifyAll()方法实现条件。而ReentrantLock可以多次调用newCondition()方法实现多个条件。

在一些内置锁(synchronized)无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的、可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁。否则,还是应该优先使用synchronized。
——选自《Java并发编程实践》Java Concurrency in Practice

在未来,比起ReentrantLock,synchronized性能更可能会被提升。

读写锁ReadWriteLock

很多程序对变量的访问,往往都是读大于写的情况。加锁之后可以保证线程安全,防止读写操作和写写操作的数据冲突,但也避免了读读操作的冲突。而读读操作在本质上是不需要加锁限制的,这样就损耗了程序的性能。读写锁就是应这种情况而产生的,放宽了加锁的粒度,允许多个线程并发执行读操作,但每次只允许一个写操作。

ReentrantReadWriteLock实现

读写锁接口ReadWriteLock的实现ReentrantReadWriteLock,对外提供两个锁,一个读锁用于读操作,执行读操作时需要获得读锁;一个写锁用于写操作,当需要修改数据时需要获得写锁。两个锁是读写锁ReentrantReadWriteLock的不同视图。

ReentrantReadWriteLock降级和升级

在ReentrantReadWriteLock中,写线程降级为读线程是可以的,但从读线程升级为写线程则是不可以的(会导致死锁)。

读写锁应用

当程序访问以读操作为主时,读写锁能够提高性能。

你可能感兴趣的:(Java锁——synchronized、ReentrantLock和ReentrantReadWriteLock)