读写锁的使用场景及锁降级

读写锁的使用场景及锁降级

  • 互斥锁
  • 读写锁的应用场景
  • 锁降级
    • 锁降级的必要性:

互斥锁

为了更好的理解读写锁的使用,首先我们需要了解一下什么是互斥锁?
对于多线程来说存在线程的不同状态,有新生状态,就绪状态,运行状态,阻塞状态,死亡状态。线程也可以通过相应命令进行相应的调度。那么为了保证数据的一致性,也就是操作的原子性,此时就需要进行锁的处理。
而所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程),原子操作是不可分割的,在执行完毕之前不会被任何其它任务或事件中断。意思就是这种操作是单位级的操作,执行过程绝对不受影响,执行结果一定。
互斥锁就是通过一种简单的加锁的方法来控制对共享资源的访问。互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。

读写锁的应用场景

我们对数据的操作无非两种:“读”和“写”,试想一个这样的情景,当十个线程同时读取某个数据时,这个操作应不应该加同步。答案是没必要的。只有以下两种情况需要加同步:
1.这十个线程对这个公共数据既有读又有写
2.这十个线程对公共数据进行写操作
也就是说有对数据进行改变的操作就需要同步上锁。
假设有这么一个场景,代码示例如下:

public Object getData(String key){  
        rw.readLock().lock();//在读前先上读锁  
        Object result = null;  
        try{  
            result = map.get(key);  
                        //这个if比较关键,它避免了多余的对数据库的读取  
            if(result==null){  
                //如果内存中没有所要数据  
                rw.readLock().unlock();  
                rw.writeLock().lock();  
                if(result==null){  
                    try{  
                       //我们用这个代替对数据库访问得到数据的步骤     
                                            result = "new";  
                    }finally{  
                    rw.writeLock().unlock();  
                    }  
                    rw.readLock().lock();  
                }  
            }  
        }finally{  
            rw.readLock().unlock();  
        }  
        return result;  
          
    }


关于unlock操作,我们知道只要是上了锁就必须要解锁,但是有这么一种情况就是当你上完锁后在执行解锁操作前程序出现异常,那这个所可能就一直存在。所以针对这个问题我们一般将unlock操作放在finally代码块中,就可以保证上了的锁一定会被解。
上面的两次if判断,第一个if相信大家很好理解。但为什么要用第二个if呢?再假设一个场景,现在有十个线程来读这个数据,而这个数据又不存在与缓存区,那么这十个线程中最先到的线程将执行“rw.writeLock().lock();”而另外九个线程将被阻塞,当第一个线程读完以后缓存区实际上已经就有了这个数据,但另外九个阻塞在“rw.writeLock().lock();”如果不加这层if他们会继续访问数据库,由此可见加了这层if对整个过程影响很大。

锁降级

锁降级指的是写锁降级成为读锁。如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级。锁降级是指把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。

修改之前的代码示例如下:

public Object getData(String key){  
        rw.readLock().lock();//在读前先上读锁  
        Object result = null;  
        try{  
            result = map.get(key);  
                        //这个if比较关键,它避免了多余的对数据库的读取  
            if(result==null){  
                //如果内存中没有所要数据  
                rw.readLock().unlock();  
                rw.writeLock().lock();  
                if(result==null){  
                    try{  
                       //我们用这个代替对数据库访问得到数据的步骤     
                                            result = "new";  
                    }finally{
                    //当前线程拥有写锁,再获取到读锁,再释放写锁
                    rw.readLock().lock();  
                    rw.writeLock().unlock();  
                    }  
                 
                }  
            }  
        }finally{  
            rw.readLock().unlock();  
        }  
        return result;  
          
    }

锁降级的必要性:

锁降级中读锁的获取是否必要呢?答案是必要的。主要是为了保证数据的可见性,如果当前线程不获取读锁而是直接释放写锁, 假设此刻另一个线程(记作线程T)获取了写锁并修改了数据,那么当前线程无法感知线程T的数据更新。如果当前线程获取读锁,即遵循锁降级的步骤,则线程T将会被阻塞,直到当前线程使用数据并释放读锁之后,线程T才能获取写锁进行数据更新。
这时因为可能存在一个事务线程不希望自己的操作被别的线程中断,而这个事务操作可能分成多部分操作更新不同的数据(或表)甚至非常耗时。如果长时间用写锁独占,显然对于某些高响应的应用是不允许的,所以在完成部分写操作后,退而使用读锁降级,来允许响应其他进程的读操作。只有当全部事务完成后才真正释放锁。
所以总结下锁降级的意义应该就是:在一边读一边写的情况下提高性能。

你可能感兴趣的:(班级作业,#博客作业)