一、互斥锁
线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁,当某个线程请求一个由其他线程持有的锁时,发出请求的线程就会阻塞。互斥锁其实提供了一种原子操作,让所有线程以串行的方式执行同步代码块。
可重入性:
某个线程试图获得一个已经由它自己持有的锁,那么这个请求就会成功(重复获取),这就叫互斥锁的可重入性。“重入”意味着获取锁的操作的粒度是“线程”,而不是调用,也就是说一个线程中可以针对同一个锁多次获取,而不需要阻塞等待。
考虑这样一个场景,ClassA里面有方法A和方法B,都是同步synchronized的方法,但是方法B中会调用方法A,如果一个线程进来调用方法B,里面调用方法A试图获取锁的时候,发现这个锁是自己线程持有的,所以不会阻塞,而是对当前前程进入的同步代码块的数目进行+1操作。这就是同一线程针对相同互斥锁的可重入性。
1、内置锁synchronized
即synchronized关键字修饰的同步代码块,是一种互斥锁。
每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁。
用法1:
class ClassB{
public synchronized void func1(){}
}
类方法用synchronized关键字修饰,和synchronized(this)一样,获取的是当前类对象的互斥锁。
用法2:
class ClassC{
public Object lockObj = new Object();
public void func1()
{
synchronized(lockObj){
}
}
}
同步代码块,在任意对象上获取的是该对象的同步锁。
2、显式锁ReentrantLock
java5.0之前,协调线程间对共享对象的访问的机制只有synchronized和volatile,但是内置锁在功能上存在一些局限性,所以java5.0增加了一种新的机制:显式锁ReentrantLock,注意它并不是替代内置锁synchronized的机制,而是当内置锁不适用时,作为一种可选的高级功能。
显式锁ReentrantLock实现了Lock接口,Lock接口提供了一种无条件的、可轮询的、定时的、可中断的锁获取操作,所有加锁和解锁的方法都是显式的。
public interface Lock {
void lock();
voidlockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(longtime, TimeUnit unit) throws InterruptedException;
void unlock();
ConditionnewCondition();
}
使用模式:
Lock lock = new ReentrantLock();
lock.lock();
try
{
//同步代码块
}finally{
lock.unlock();
}
高级加锁功能:
1、 轮询锁与定时锁
2、 可中断锁
3、 非块结构的加锁
公平性:
public ReentrantLock()
public ReentrantLock(boolean fair)
显式锁ReentrantLock提供的构造函数可以选择公平性:公平锁(默认)和非公平锁。
(1) 创建一个公平的锁表示线程将按照他们发出获取锁请求的顺序来获得锁。
(2) 创建一个非公平锁,表示某个线程A发出获取锁的请求,恰好这个锁被释放了,那么这个线程A将直接“插队”跳过队列中所有等待线程获得这个锁,因为从队列中唤醒线程时需要时间的,可能唤醒了之后A线程已经执行完成并释放锁了,这样是一种“双赢”的局面,极大提升系统性能。
– 内置锁和显式锁之间的选择
java5.0中内置锁相对于显式锁性能非常低,但是到了Java6.0之后,两者性能基本相当。
显式锁在加锁和内存上提供和内置锁相同的语义,但是增加了诸如定时锁、可中断锁、公平性、以及实现非块结构锁等功能,是否应该直接放弃内置锁只使用显式锁呢?
答案是否定的。(1)未来可能还会提供内置锁性能而不是显式锁性能,因为内置锁是JVM内置属性,针对内置锁做了一些优化;(2)内置锁语法简洁紧凑,为开发人员更为熟悉。
原则:
总之优先考虑使用内置锁,在需要一些高级功能而内置锁无法满足需求的情况下,才可以考虑使用显式锁。
二、读写锁
很明显互斥锁是一种过于保守的加锁规则,写写冲突、写读冲突有必要避免,但是读读之间是没有冲突的,所以需要一种更加宽松的加锁规则,由此出现了读写锁ReadWriteLock:一个资源可以被多个读操作访问,或者被一个写操作访问,但是两者不能同时进行。
显示读写锁ReentrantReadWriteLock类实现了ReadWriteLock接口:
public interface ReadWriteLock {
Lock readLock(); // Returnsthe lock used for reading
Lock writeLock(); // Returnsthe lock used for writing
}
可以看到实际上就是提供了两个方法,分别返回一个读锁和一个写锁。