Skip to content

Java 读写锁

可同时读,读的时候不能写,写的时候不能读。读-写锁实现的加锁策略中,允许多个读操作同时进行,但每次只允许一个写操作。

使用ReentrantReadWriteLock封装的Map示例

ReentrantReadWriteLock在构造时可以选择是否为公平锁。 这里给出一个ReentrantReadWriteLock封装的Map的示例。

import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 使用ReentrantReadWriteLock封装的Map
 */
public class ReadWriteMap<K, V> {
    private final Map<K, V> map; // 当然我们也可以封装一些别的集合类
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock r = lock.readLock();
    private final Lock w = lock.writeLock();

    public ReadWriteMap(Map<K, V> map) {
        this.map = map;
    }

    public V put(K key, V value) {
        w.lock();
        System.out.println(Thread.currentThread() + " is putting."); // for debug
        try {
            return map.put(key, value);
        } finally {
            w.unlock();
        }
    }

    public V get(Object key) {
        r.lock();
        try {
            return map.get(key);
        } finally {
            r.unlock();
        }
    }

}

remove()putAll()clear()等方法执行相同的操作。

测试代码

public class ReadWriteLockDemo {

    public static void main(String[] args) {
        final ReadWriteMap<String, String> map = new ReadWriteMap<>(new HashMap<>());
        map.put("one", "init value");
        for (int i = 1; i <= 4; i++) {
            new WorkThread(map).start();
        }
    }

    // 模拟工作的线程
    static class WorkThread extends Thread {

        private ReadWriteMap<String, String> map;

        public WorkThread(ReadWriteMap<String, String> map) {
            this.map = map;
        }

        @Override
        public void run() {
            for (int i = 1; i <= 2; i++) {
                try {
                    System.out.println(this.toString() + " read out: " + map.get("one"));
                    map.put("one", this.toString() + System.currentTimeMillis());
                    Thread.sleep(2);
                    System.out.println(this.toString() + " read out: " + map.get("one"));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

/* 部分输出
Thread[main,5,main] is putting.
Thread[Thread-1,5,main] read out: init value
Thread[Thread-2,5,main] read out: init value
Thread[Thread-1,5,main] is putting.
Thread[Thread-2,5,main] is putting.
Thread[Thread-3,5,main] read out: Thread[Thread-2,5,main]1536664817446
Thread[Thread-3,5,main] is putting.
Thread[Thread-0,5,main] read out: Thread[Thread-3,5,main]1536664817446
Thread[Thread-0,5,main] is putting.
Thread[Thread-2,5,main] read out: Thread[Thread-0,5,main]1536664817447
Thread[Thread-2,5,main] read out: Thread[Thread-0,5,main]1536664817447
Thread[Thread-3,5,main] read out: Thread[Thread-0,5,main]1536664817447
*/

ReentrantLock不能完全替代synchronized,只有在synchronized无法满足需求时,才应该使用它。

读-写锁允许多个读线程并发地访问被保护的对象,当访问以读取操作为主的数据结构时, 它能提高程序的可伸缩性。

参考

  • 《Java并发编程实战》 Java Concurrency in Practice