1.ReentrantLock的限制
ReentrantLock保证了只有一个线程可以执行临界区代码。
临界区代码:任何时候只有1个线程可以执行的代码块。
临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待,有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用,例如:semaphore。只能被单一线程访问的设备,例如:打印机。
public void inc(){
lock.lock();
try{
value += 1;
}finally{
lock.unlock();
}
}
public int get(){
lock.lock();
try(){
return value;
}finally{
lock.unlock();
}
}
但有时候,这种保护有些过头:
- 因为我们发现任何时刻只允许1个线程修改,对inc()进行加锁是必须的。
- 但是get()方法只读取数据,不修改数据。它应该允许多个线程同时调用。
我们期望:允许多个线程同时读,但只要有一个线程在写,其他线程就必须等待。
2. ReadWriteLock
使用ReadWriteLock可以解决:
- 只允许一个线程写入(其他线程既不能写入,也不能读取)
- 没有写入时,多个线程允许同时读(提高性能)
- 实现
class Counter{
final ReadWriteLock lock = new ReentrantReadWriteLock();
//分别获得readLock和writeLock
final Lock rlock = lock.readLock();
final Lock wlock = lock.writeLock();
private int value = 0;
public void inc(){
wlock.lock();
try{
value += 1;
}finally {
wlock.unlock();
}
}
public int get(){
rlock.lock();
try{
return this.value;
}finally {
rlock.unlock();
}
}
}
3.示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Count{
private ReadWriteLock lock = new ReentrantReadWriteLock();
private Lock rlock = lock.readLock();
private Lock wlock = lock.writeLock();
private int value = 0;
public void add(int m){
wlock.lock();
try{
value += m;
}finally {
wlock.unlock();
}
}
public void dec(int m){
wlock.lock();
try{
value -= m;
}finally {
wlock.unlock();
}
}
public int get(){
rlock.lock();
try{
return value;
}finally {
rlock.unlock();
}
}
}
public class Main{
final static int LOOP = 100;
public static void main(String[] args) throws Exception{
Count count = new Count();
Thread t1 = new Thread(){
public void run(){
for(int i=0;i<LOOP;i++){
count.add(1);
}
}
};
Thread t2 = new Thread(){
public void run(){
for(int i=0;i<LOOP;i++){
count.dec(1);
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count.get());
}
}
ReadWriteLock适用条件:
- 同一个实例,有大量线程读取,仅有少数线程修改。
- 例如论坛帖子,回复视为写入,不频繁操作;浏览器看作读取,非常频繁。这种情况下,就可以适用ReadWriteLock
4. 总结:
使用ReadWriteLock可以提高读取效率:
- ReadWriteLock只允许一个线程写入。
- ReadWriteLock允许多个线程同时读取。
- ReadWriteLock适合读多写少的场景。