java特性的锁只有两种,volatile和synchronized,其他的都是lib包实现。
synchronized
对象锁,指定对象加锁,钥匙有且只有一个,线程之间会竞争这个钥匙,谁拿到先谁就可以进入,完事后归还该锁,循环往复。没争抢到的则被阻塞。
程序员没法控制这种锁的状态,线程获取锁后中断导致被锁对象死锁,并且极端情况程序间相互调用产生互斥。
用法
class Lock{
//因为静态method加载顺序比class优先,所以静态方法中需要反射
public static synchronized void Test3() {
synchronized(Lock.class){
//todo
}
}
//本质是锁当前方法
public synchronized void Test1() {
//todo
}
//锁lockObj属性
private Boolean lockObj = true;
public synchronized void Test2() {
synchronized(lockObj){
//todo
}
}
}
reentrantlock
针对synchronized的弊端,延伸出可控制获取锁状态的类,使得锁的获取和释放可在代码中控制,那意味着可在finally中释放,避免程序异常导致死锁。
class Counter {
private final Lock lock = new ReentrantLock();
private int count;
//程序异常可以释放锁,防止形成死锁
public void add(int n) {
lock.lock();
try {
count += n;
} finally {
lock.unlock();
}
}
//尝试获取锁,超时返回false
public void add2(int n) throws InterruptedException {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
//todo
} finally {
lock.unlock();
}
}
}
}
ReadWriteLock
这种锁是优化reentrantlock的使用,reentrantlock是针对线程访问的锁,ReadWriteLock是针对读写操作细分的锁,它可以更精细化的控制锁的粒度。读写锁分离。
public class Counter {
private final ReadWriteLock rwlock = new ReentrantReadWriteLock();
private final Lock rlock = rwlock.readLock();
private final Lock wlock = rwlock.writeLock();
public void inc(int index) {
wlock.lock(); // 加写锁
try {
//todo
} finally {
wlock.unlock(); // 释放写锁
}
}
public int[] get() {
rlock.lock(); // 加读锁
try {
//todo
} finally {
rlock.unlock(); // 释放读锁
}
}
}
ps:读锁如何用于优化性能呢?比如一条队列,长度为0时候加上读锁,获取队列长度这种操作就可以无需执行。
StampedLock
这种锁在你读的过程中不允许写入,StampedLock和ReadWriteLock相比,读写锁是可以读锁和写锁一起获取,同时读写的,但是StampedLock不允许同时读写
volatile
说这个锁之前,先解释下为什么会有线程不安全,线程操作一个共享资源的值时候,会把这个资源重内存加载到cpu寄存器(高速缓存),然后操作完成会刷回内存,并发过程中,多个线程一起拿一起操作同一个值,一起刷回去,就导致了操作不幂等了。
有了这个概念就很好解释volatile,它实际就是让你的资源在cpu高速缓存中保证一致性,但也存在弊端,被volatile锁的资源如果操作中含有其他共享资源(包括它自己)也会导致线程不安全。
class VolatileCounter{
//正确打开方式
private Boolean status;
public void open(){
status = true;
}
public void close(){
status = false;
}
//错误例子
private Integer otherShareNumber; //该对象被其他线程操作
private volatile Integer number;
public void add(){
number = number + 1;
number = otherShareNumber + 1;
}
}
AtomicX
X是各种类型,底层也是基于volatile,这里引入乐观锁的概念,每次更新时候都比对一下旧值,旧值一样才允许更新,否则再来一遍,直到更新成功为止。
public int incrementAndGet(AtomicInteger var) {
int prev, next;
do {
prev = var.get();
next = prev + 1;
} while ( ! var.compareAndSet(prev, next));
return next;
}