Java中简单的读写锁实现

转载自并发编程网 原文地址:Java中的读写锁

多线程读写共享资源的规则:
1.已有线程在进行read时,当前线程的read操作不等待,而write需要等待
2.已有线程在进行write时,当前线程read或者write都需要等待

简单的读写锁实现:

public class ReadWriteLock {                                           
    private int readers = 0;                                           
    private int writers = 0;                                           
    private int writeRequests = 0;                                     

    public synchronized void lockRead() throws InterruptedException {  
        while (writers > 0 || writeRequests > 0) {                     
            wait();                                                    
        }                                                              

        readers++;                                                     
    }                                                                  

    public synchronized void unlockRead() {                            
        readers--;                                                     
        notifyAll();                                                   
    }                                                                  

    public synchronized void lockWrite() throws InterruptedException { 
        writeRequests++;                                               

        while (readers > 0 || writers > 0) {                           
            wait();                                                    
        }                                                              

        writeRequests--;                                               
        writers++;                                                     
    }                                                                  

    public synchronized void unlockWrite() {                           
        writers--;                                                     
        notifyAll();                                                   
    }                                                                  
}                                                                      

原文中的读写锁中优先写操作,在写操作的请求比较多的情况下会导致读取一直无法进行,所以可以添加一个标志位以切换优先级。

public class ReadWriteLock {                                            
    private int readers = 0;                                            
    private int writers = 0;                                            
    private int writeRequests = 0;                                      
    private boolean preferWriter = true;                                

    public synchronized void lockRead() throws InterruptedException {   
        while (writers > 0 || (preferWriter && writeRequests > 0)) {    
            wait();                                                     
        }                                                               

        readers++;                                                      
    }                                                                   

    public synchronized void unlockRead() {                             
        readers--;                                                      
        preferWriter = true;                                            
        notifyAll();                                                    
    }                                                                   

    public synchronized void lockWrite() throws InterruptedException {  
        writeRequests++;                                                

        while (readers > 0 || writers > 0) {                            
            wait();                                                     
        }                                                               

        writeRequests--;                                                
        writers++;                                                      
    }                                                                   

    public synchronized void unlockWrite() {                            
        writers--;                                                      
        preferWriter = false;                                           
        notifyAll();                                                    
    }                                                                   
}                                                                       

锁的重入问题
内置锁本身是可重入的,如果要在自己实现的简单独占锁中支持可重入,就需要一个计数器字段,否则在释放锁的时候无法知道线程是否在释放最后一次的锁定。
所以在读写锁中要记录已经获取锁定的线程数量,在同一时间最多只会有一个写线程获取写操作的锁定,所以只要一个字段记录当前的写线程。而在同时会有多个读线程获取锁定或者重入获取锁,必须要每个读线程获取锁的次数,否则在释放读锁的时候还是无法知道线程是否在释放最后一次的锁定。

锁重入时的规则:
1.已获取读锁时,写线程被迫等待,所以可以再次获取读锁,而且如果不允许再次获取的话,当前已有的读锁无法释放,会出现死锁的情形;
2。已获取写锁时,读线程都被迫等待,所以可以再次获取写锁,如果不允许再次获取的话,当前已有的写锁无法释放,也会出现死锁的情形。

实现可重入是在每次判断线程是否需要等待时新增了一种情况,当能确认是线程重入时则继续往下执行。

public class ReentrantReadWriteLock {                                                                                     
    private int writers = 0;                                                                                              
    private int writeRequests = 0;                                                                                        
    private boolean preferWriter = true;                                                                                  
    private Thread writingThread = null;                                                                                  
    private Map<Thread, Integer> readingThreads = new HashMap<Thread, Integer>();                                         

    public synchronized void lockRead() throws InterruptedException {                                                     
        Thread callingThread=Thread.currentThread();                                                                      
        while (!canGrantReadAccess(callingThread)) {                                                                      
            wait();                                                                                                       
        }                                                                                                                 

        int accessCount = getReadAccessCount(callingThread);                                                              
        readingThreads.put(callingThread, accessCount + 1);                                                               
    }                                                                                                                     

    public synchronized void unlockRead() {                                                                               
        Thread callingThread = Thread.currentThread();                                                                    
        if (!isReader(callingThread)) {                                                                                   
            throw new IllegalMonitorStateException("Calling thread does not hold a read lock on this ReadWriteLock");     
        }                                                                                                                 

        int accessCount = getReadAccessCount(callingThread);                                                              
        if (accessCount == 1) {                                                                                           
            readingThreads.remove(callingThread);                                                                         
            preferWriter = true;                                                                                          
        } else {                                                                                                          
            readingThreads.put(callingThread, accessCount - 1);                                                           
        }                                                                                                                 

        notifyAll();                                                                                                      
    }                                                                                                                     

    public synchronized void lockWrite() throws InterruptedException {                                                    
        writeRequests++;                                                                                                  

        Thread callingThread=Thread.currentThread();                                                                      
        while (!canGrantWriteAccess(callingThread)) {                                                                     
            wait();                                                                                                       
        }                                                                                                                 

        writeRequests--;                                                                                                  
        writers++;                                                                                                        
        writingThread = callingThread;                                                                                    
    }                                                                                                                     

    public synchronized void unlockWrite() {                                                                              
        Thread callingThread = Thread.currentThread();                                                                    
        if (!isWriter(callingThread)) {                                                                                   
            throw new IllegalMonitorStateException("Calling thread does not hold a write lock on this ReadWriteLock");    
        }                                                                                                                 

        writers--;                                                                                                        
        if (writers == 0) {                                                                                               
            writingThread = null;                                                                                         
            preferWriter = false;                                                                                         
        }                                                                                                                 

        notifyAll();                                                                                                      
    }                                                                                                                     

    private boolean canGrantReadAccess(Thread callingThread) {                                                            
        if (writers > 0)                                                                                                  
            return false;                                                                                                 
        if (isReader(callingThread))                                                                                      
            return true;                                                                                                  
        if (preferWriter && writeRequests > 0) // 相对位置要在上一个判断条件之后,第一个判断条件的位置没有关系                                          
            return false;                                                                                                 

        return true;                                                                                                      
    }                                                                                                                     

    private boolean canGrantWriteAccess(Thread callingThread) {                                                           
        if (hasReaders())                                                                                                 
            return false;                                                                                                 
        if (isWriter(callingThread))                                                                                      
            return true;                                                                                                  
        if (writers > 0) // 相对位置要在上一个判断条件之后,第一个判断条件的位置没有关系                                                                
            return false;                                                                                                 

        return true;                                                                                                      
    }                                                                                                                     

    private boolean hasReaders() {                                                                                        
        return readingThreads.size() > 0;                                                                                 
    }                                                                                                                     

    private boolean isReader(Thread callingThread) {                                                                      
        return readingThreads.get(callingThread) != null;                                                                 
    }                                                                                                                     

    private int getReadAccessCount(Thread callingThread) {                                                                
        Integer accessCount = readingThreads.get(callingThread);                                                          
        if (accessCount == null)                                                                                          
            return 0;                                                                                                     
        return accessCount.intValue();                                                                                    
    }                                                                                                                     

    private boolean isWriter(Thread callingThread) {                                                                      
        return writingThread == callingThread;                                                                            
    }                                                                                                                     
}                                                                                                                         

读锁升级到写锁
已经获取读锁的线程再次获取写锁时,和普通的写锁一样,需要等到其他线程的锁定都释放,只有当前一个线程时才能够获取到。

private boolean canGrantWriteAccess(Thread callingThread) {
    if (isOnlyReader(callingThread))                       
        return true;                                       
    if (hasReaders())                                      
        return false;                                      
    if (isWriter(callingThread))                           
        return true;                                       
    if (writers > 0)                                       
        return false;                                      

    return true;                                           
}  

private boolean isOnlyReader(Thread callingThread) {              
    return isReader(callingThread) && readingThreads.size() == 1; 
}                                                                                                                    

写锁降级到读锁
已经获取到写锁,再去获取读锁时,不会有其他的线程获取到锁定,所以可以获取到读锁。

private boolean canGrantReadAccess(Thread callingThread) {
    if (isWriter(callingThread))                          
        return true;                                      
    if (writers > 0)                                      
        return false;                                     
    if (isReader(callingThread))                          
        return true;                                      
    if (preferWriter && writeRequests > 0)                
        return false;                                     

    return true;                                          
}                                                         

释放锁时需要使用notifyAll()方法,以防止信号丢失的问题。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java读写锁是通过`ReentrantReadWriteLock`类实现的。这个类提供了两个锁,一个读锁和一个写锁,可以通过它们来实现对共享数据的读写操作。 下面是一个简单的示例代码,演示了如何使用读写锁实现对共享数据的读写操作。 ```java import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockDemo { private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private static int count = 0; public static void main(String[] args) { new Thread(() -> { while (true) { try { lock.writeLock().lock(); count++; System.out.println(Thread.currentThread().getName() + " write count = " + count); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.writeLock().unlock(); } } }, "write-thread").start(); new Thread(() -> { while (true) { try { lock.readLock().lock(); System.out.println(Thread.currentThread().getName() + " read count = " + count); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.readLock().unlock(); } } }, "read-thread").start(); } } ``` 在这个例子,我们创建了一个`ReentrantReadWriteLock`对象作为共享锁,并定义了一个共享变量`count`。然后,我们创建了两个线程,一个写线程和一个读线程。写线程每隔一秒钟增加一次`count`的值,并打印出来,而读线程每隔一秒钟读取`count`的值,并打印出来。 在写线程,我们使用`lock.writeLock().lock()`获取写锁,并在使用完共享变量后,使用`lock.writeLock().unlock()`释放写锁。而在读线程,我们使用`lock.readLock().lock()`获取读锁,并在使用完共享变量后,使用`lock.readLock().unlock()`释放读锁。这样就可以保证多个读线程可以同时读取共享变量,而写线程需要独占锁。 需要注意的是,写锁是独占锁,在写锁被持有期间,其他线程无法获取写锁和读锁,而读锁是共享锁,在读锁被持有期间,其他线程可以同时获取读锁,但不能获取写锁。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值