Lock接口与synchronized关键字的区别:
1.使用synchronized关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放。当然,这种方式简化了同步的管理,可是扩展性没有显示的锁获取和释放来的好。
2.Lock接口(以及相关实现类)提供了与synchronized关键字类似的同步功能,只是在使用时需要显式地获取和释放锁。虽然它缺少了(通过synchronized块或者方法所提供的)隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的可操作性、可中断的获取锁以 及超时获取锁等多种synchronized关键字所不具备的同步特性。
Lock使用示例:
Lock lock = new ReentrantLock();
lock.lock();
try{
do something....
}finally {
lock.unlock();
}
1.在finally块中释放锁,目的是保证在获取到锁之后,最终能够被释放。
2.不要将获取锁的过程写在try块中,因为如果在获取锁(自定义锁的实现)时发生了异常,异常抛出的同时,也会导致锁无故释放。
重入锁:
1.重入锁ReentrantLock,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。
2.而synchronized关键字隐式的支持重进入,比如一个synchronized修饰的递归方 法,在方法执行时,执行线程在获取了锁之后仍能连续多次地获得该锁。
3.ReentrantLock虽然没能像synchronized关键字一样支持隐式的重进入,但是在调用lock()方 法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。
公平锁与非公平锁:
1.如果在绝对时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。公平的获取锁,也就是等待时间最长的线 程最优先获取锁,也可以说锁获取是顺序的。2.ReentrantLock提供了一个构造函数,能够控制锁是否是公平的。
3.公平的锁机制往往没有非公平的效率高,但是公平锁能够减少“饥饿”发生的概率,等待越久的请求越是能够得到优先满足。
4.公平性锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平性锁虽然可能造成线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量。
读写锁
1.排他锁:排他锁在同一时刻只允许一个线程进行访问,大部分锁(如Mutex和ReentrantLock)基本都是排他锁。
2.读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。
3.一般情况下,读写锁的性能都会比排它锁好,因为大多数场景读是多于写的。在读多于写 的情况下,读写锁能够提供比排它锁更好的并发性和吞吐量。Java并发包提供读写锁的实现是 ReentrantReadWriteLock
Condition接口:
任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait()、 wait(long timeout)、notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,可以 实现等待/通知模式。Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等 待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。
使用示例:
package cn.itcast.day06.demo6;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Solution {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void conditionWait() throws InterruptedException {
lock.lock();
try{
condition.await();
}finally {
lock.unlock();
}
}
public void conditionSignal(){
lock.lock();
try {
condition.signal();
}finally {
lock.unlock();
}
}
}
一般都会将Condition对象作为成员变量。当调用await()方法后,当前线程会 释放锁并在此等待,而其他线程调用Condition对象的signal()方法,通知当前线程后,当前线程 才从await()方法返回,并且在返回前已经获取了锁。