多线程(11) — Lock,ReentrantLock&ReadWrite读写锁

大体来说Lock、ReentrantLock与Synchronized很相似,对对象加锁

首先是Lock接口:

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

lock()上锁、unlock()解锁、
trylock()获取锁,若被其它线程获取则返回,这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
lockInterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,是可以响应中断的。而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。

ReentrantLock是Lock接口的一个实现类,ReentrantLock自然包含Lock的所有功能:

/**
 * Created by qiaorenjie on 2018/3/23.
 */
public class LockTest {

    public static void main(String[] args) {
        final Phone phone = new Phone();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    phone.getPhone("iphone-x plus");
                }
            }
        }).start();

        new Thread(){
            @Override
            public void run() {
                while (true){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    phone.getPhone("小米特别版");
                }
            }
        }.start();

    }

    static class Phone {
        Lock lock = new ReentrantLock();
        public void getPhone(String phone){
            int len = phone.length();
            lock.lock();      // 开始加锁
            try {
                for (int i=0; i

这是Lock的ReentrantLock一个典型应用。

接下来是Lock下的读写锁:读写锁,分为读锁和写锁,多个读锁间不互斥,但凡涉及到写锁就要互斥。如果代码允许很多人同时读,但不能同时写,那就上读锁;如果修改代码数据,同时只能一个人修改,且不能同时读取,那就要上读写锁。jvm会自动完成操作

读写锁部分很重要。

ReadWriteLock源码如下:

public interface ReadWriteLock {
   
    Lock readLock();

    Lock writeLock();
}

可见ReadWriteLock接口只有读锁和写锁两个方法,通过ReentrantReadWriteLock实现(意外发现Lock的实现类也是叫Reentrant....所以Reentrant可能是各个接口实现类的专属命名方式吧)


多线程(11) — Lock,ReentrantLock&ReadWrite读写锁_第1张图片
image.png

经典应用题:写一个缓存类,对数据进行get,读与写。

/**
 * Created by qiaorenjie on 2018/3/30.
 */
public class CacheDemo {

    private Map cache = new HashMap<>();

    public static void main(String[] args) {
    }

    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public Object getData(String key){
        readWriteLock.readLock().lock();
        Object value = null;      // 让value在try内可见,数据的操作在try里这样总是在finally里unlock
        try {
            value = cache.get(key);
            if (value == null){
                readWriteLock.readLock().unlock();   //发现内容为空就要写,释放读锁加写锁不让其他来读
                readWriteLock.writeLock().lock();    //不管其他几个线程只要这加了写锁,就不能读了
                try {
                    if(value == null) {    // 第一个线程写了第二个线程发现后就不用在写了
                        value = "aaaaa";   // 假设从数据库queryDB();
                    }
                }finally {
                    readWriteLock.writeLock().unlock();
                }
                readWriteLock.readLock().lock();  //回复读锁恢复原状
            }
        }finally {
            readWriteLock.readLock().unlock();
        }

        return value;
    }

}

读的时候发现为空那么此时就要释放读锁加写锁进行写的操作,当加了写锁之后其他线程都不能进行读的操作,只能写,写完后再加读锁保证数据可读恢复原状。假如三个线程,第一个线程写完之后加读锁然后释放,第二个线程读锁再进入,如果写过了就不用再写。
相比直接在方法上加synchronized读写锁更加细粒度,更加有效率。

你可能感兴趣的:(多线程(11) — Lock,ReentrantLock&ReadWrite读写锁)