可重入锁
可重入锁,也叫做递归锁,指的是在同一线程内,外层函数获得锁之后,内层递归函数仍然可以获取到该锁。换一种说法:同一个线程再次进入同步代码时,可以使用自己已获取到的锁。
主要作用是避免思索以及节省获取锁的时间。
Synchronized 和 ReentrantLock都是可重入锁。
public class SynchronizedTest {
public static void main(String[] args) {
SynchronizedTest synchronizedTest = new SynchronizedTest();
synchronizedTest.tell();
}
public synchronized void tell() {
System.out.println("进入tell");
tell2();
}
public synchronized void tell2() {
System.out.println("进入tell2");
}
}
//结果是
//进入tell
//进入tell2
读写锁
适用于程序中写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写例如:(读-读能共存,读-写不能共存,写-写不能共存)。
java5在java.util.concurrent包中提供了读写锁的实现,ReentrantReadWriteLock
悲观锁
悲观的认为每次取数据时都认为其他线程会修改,所以都会加锁,当其他线程想要访问数据时,都需要阻塞挂起。Java中synchronized的思想也是悲观锁。
乐观锁
乐观的认为不会发生并发操作,所以本质是没有锁,所以会效率比较高,无阻塞。
他的实现是在更新数据的时候判断有没有对数据及进行修改。一般会使用version机制实现。
version方式
一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当修改的时候查看version是否改变,如果没改变则修改成功,否则重试修改,直到更新成功。
select version from table where x=y;
update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
CAS无锁(Compare And Swap)
与锁相比,使用比较交换(下文简称CAS)会使程序看起来更加复杂一些。但由于其非阻塞性,它对死锁问题天生免疫,并且,线程间的相互影响也远远比基于锁的方式要小。更为重要的是,使用无锁的方式完全没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销,因此,它要比基于锁的方式拥有更优越的性能。
java5后的并发包提供了几个CAS实现的原子类
- AtomicBoolean
- AtomicInteger
- AtomicLong
- AtomicReference
小Tip:在java早期版本中,Synchronized是重量级锁,因为底层依赖操作系统的Mutex Lock的,java的线程池是映射到系统中的原生线程上的,如果要挂起或者唤醒一个线程,都需要操作系统帮助完成,操作系统完成线程的去切换需要从用户态转换为内核态,事件成本较高,这也是为什么java6之前Synchronized效率低下的原因。
从java6之后,官方对sychronized引入了大量优化,包括偏量锁,轻量级锁、自旋锁,适应性自旋锁、锁消除、锁粗化等技术,用于减少锁的开销。
锁主要有4个状态,一次是无锁、轻量级锁、自旋锁、重量级锁。它们会随着竞争激烈而逐渐升级,且只可升级,不可降级。