《线程安全之ReadWriteLock》
synchronized关键字可以用于修饰方法和对象,被修饰的方法和对象在某一时间内只允许任意一条线程对其进行访问操作,这就是典型的排它锁。那么假设一个对象内部的某个数据类型同时拥有读/写方法时,读操作的权重比例明显高于写操作时,那么为了保证线程安全,我们都会加上synchronized关键字进行修饰,那么程序在并发环境下的吞吐量将会大大降低。因为从理论上来说,并发对同一数据进行写操作时,肯定是非线程安全的,但是读操作却是允许的,因此synchronized关键字并不能满足开发人员的需要。
为了解决上述问题,Java5开始提供读写锁API,也就是ReadWriteLock接口,其派生类为ReentrantReadWriteLock。在并发环境下,当某一个线程正在试图访问对象内部的某一个数据类型的写入方法时,其余线程只能选择阻塞等待。而当某一个线程正在访问对象内部的某一个数据类型的读操作时,尽管其余线程并不可以同时访问这个数据类型的写入方法,但却可以访问读方法,这就读写锁的作用。
示例代码,如下所示:
public class LockTest {
public static void main(String[] args) {
final Resource r = new Resource();
/* 当触发读锁时,方法只读,不可写 */
// new Thread() {
// public void run() {
// r.getValue("ThreadA");
// }
// }.start();
// new Thread() {
// public void run() {
// r.getValue("ThreadB");
// }
// }.start();
/* 当触发写锁时,方法不可读/写 */
new Thread() {
@Override
public void run() {
r.setValue("ThreadA");
}
}.start();
new Thread() {
@Override
public void run() {
r.getValue("ThreadB");
}
}.start();
}
}
class Resource {
ReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();
public void getValue(String threadName) {
try {
readLock.lock();
for (int i = 0; i < 100; i++) {
System.out.println(threadName + i);
}
} finally {
readLock.unlock();
}
}
public void setValue(String threadName) {
try {
writeLock.lock();
System.out.println(threadName + "\tWrite...");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
}