java的几种锁

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;
}

你可能感兴趣的:(java的几种锁)