private final ReentrantReadWriteLock theLock = new ReentrantReadWriteLock();
private final Lock writeLock = theLock.writeLock();
private final Lock readLock = theLock.readLock();
能创建读锁和写锁,我搞不清楚读写锁的区别,分别用在什么场合,好像我全用读锁也能实现锁住方法
public static void main(String[] args) {
final Test t = new Test();
scheduledExecutor = new ScheduledThreadPoolExecutor(3);
scheduledExecutor.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
t.writeLock.lock();
t.print("test1111111111111");
t.writeLock.unlock();
} catch (Throwable t) {
}
}
}, 5, 10000, TimeUnit.SECONDS);
scheduledExecutor.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
t.readLock.lock();
t.print("test2222222222222222");
t.readLock.unlock();
} catch (Throwable t) {
}
}
}, 10, 1000, TimeUnit.SECONDS);
scheduledExecutor.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
t.writeLock.lock();
t.print("test33333333333");
t.writeLock.unlock();
} catch (Throwable t) {
}
}
}, 5, 10000, TimeUnit.SECONDS);
}
public void print(String s){
System.out.println(s);
while(true)
;
}
能保证一个时刻只有一个线程能进入此方法,到底读写锁有什么区别呢,什么时候用读的,什么时候用写的
[b]问题补充:[/b]
我试过,如果读锁被获取,试图获取读写锁都阻塞了.还有,有些情况是读写交错的,比如有一个方法,先是从库里读一些数据过来,然后,再写到数据库里.那么你说这时用读锁还是写锁.
[b]问题补充:[/b]
这遍文章我看过,我不明白,具体在什么时候用读写锁
此类具有以下属性:
获取顺序
此类不会将读取者优先或写入者优先强加给锁定访问的排序。但是,它确实支持可选的公平 策略。当公平地构造线程时,线程利用一个近似到达顺序的策略来争夺进入。当释放写入锁定后,将写入锁定分配给等待时间最长的单个写入者,如果有一个等待时间比所有写入者更长的读取者,则将读取锁定分配给读取者 set。当非公平地构造线程时,则不需要按照到达顺序进入锁定。不管是哪一种情况,如果读取者处于活动状态,而某个写入者进入锁定状态,那么在获取写入者并释放写入锁定之前,不会将读取锁定授予任何后续的读取者。
重入
此锁定允许读取者和写入者按照 ReentrantLock 的样式重新获取读取锁定或写入锁定。在写入线程保持的所有写入锁定都已经释放后,才允许写入者使用它们。
此外,写入者可以获取读取锁定——但反过来则不成立。在其他应用程序中,当在调用或回调那些在读取锁定状态下执行读取操作的方法期间保持写入锁定时,重入很有用。如果读取者试图获取写入锁定,那么将永远不会获得成功。
锁定降级
重入还允许从写入锁定降级为读取锁定,其实现方式是:先获取写入锁定,然后获取读取锁定,最后释放写入锁定。但是,从读取锁定升级到写入锁定是不可能的。
锁定获取的中断
读取锁定和写入锁定都支持锁定获取期间的中断。
Condition 支持
写入锁定提供了一个 Condition 实现,对于写入锁定来说,该实现的行为与 ReentrantLock.newCondition() 提供的 Condition 实现对 ReentrantLock 所做的行为相同。当然,此 Condition 只能用于写入锁定。
读取锁定不支持 Condition,readLock().newCondition() 会抛出 UnsupportedOperationException。
监测
此类支持一些确定是保持锁定还是争用锁定的方法。这些方法设计用于监视系统状态,而不是同步控制。
此类行为的序列化方式与内置锁定的相同:反序列化的锁定处于解除锁定状态,无论序列化该锁定时其状态如何。
示例用法。下面的代码展示了如何利用重入来执行升级缓存后的锁定降级(为简单起见,省略了异常处理):
class CachedData {
Object data;
volatile boolean cacheValid;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// upgrade lock manually
rwl.readLock().unlock(); // must unlock first to obtain writelock
rwl.writeLock().lock();
if (!cacheValid) { // recheck
data = ...
cacheValid = true;
}
// downgrade lock
rwl.readLock().lock(); // reacquire read without giving up write lock
rwl.writeLock().unlock(); // unlock write, still hold read
}
use(data);
rwl.readLock().unlock();
}
}
在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。
class RWDictionary {
private final Map m = new TreeMap();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock(); try { return m.get(key); } finally { r.unlock(); }
}
public String[] allKeys() {
r.lock(); try { return m.keySet().toArray(); } finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock(); try { return m.put(key, value); } finally { w.unlock(); }
}
public void clear() {
w.lock(); try { m.clear(); } finally { w.unlock(); }
}
}
实现注意事项:
重入写入锁定定义了一个所有者,但只有获取它的线程才能将它释放。相反,在此实现中,读取锁定没有所有权这一概念,并不要求释放读取锁定的线程与获取该锁定的线程是同一线程。但是,无法保证此类以后的实现中会保持此属性。
此锁定最多支持 65536 个递归写入锁定和 65536 个读取锁定。试图超出这些限制将导致锁定方法抛出 Error。
读写锁主要是用在读写次数相差很多,读得多写得少的情况。在保护一个资源时,如果某块代码只涉及读取资源的状态,而不会改变资源的状态,就可以用读锁;反之则用写锁。如果当前没有写锁被获取,那么试图获取读锁的操作都不会被阻塞。如果当前有写锁被获取了,则再试图获取写锁和读锁都会被阻塞,直到之前锁住的写锁被释放。这样既保证了状态的一致性,又保持了多线程同时读取状态的性能不因为锁而下降。