• 显示锁


    之前在协调对共享对象的访问时可以使用的机制只有synchronized和volatile。java5.0增加了一种新的机制:ReentrantLock。

     
    Lock和ReentrantLock
    Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显示的。
    ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。ReentrantLock可重入加锁。
    Lock接口的使用形式:
      
      Lock lock = new ReentrantLock();
        . . .
       lock.lock();
       try{
           //更新对象状态
           //捕获异常,并在必要时恢复不变性条件
       }finally{
           lock.unlock();
       }
    说明:1.使用形式比内置锁复杂;
               2.使用try-catch或try-finally代码块包含需要加锁的代码块,捕捉异常;
               3.必须在finally块中释放锁,否则,如果被保护的代码块抛异常,锁一直无法释放;
               
    轮询锁和定时锁
    可轮询的与可定时的锁获取模式是由tryLock实现的,具有更完善的错误恢复机制。
    内置锁的死锁恢复只能靠重新启动程序,防止死锁的唯一方法就是在构造程序时避免出现不一致的锁顺序。
    可定时的与可轮询的锁提供了另一种选择:避免死锁的发生。如果不能获取所有的锁,它会使你重新获得控制权,释放已经获得的锁,然后重新尝试获得所有锁。
    当使用内置锁时,在开始请求锁后,这个操作将无法取消,因此内置锁很难实现带有时间限制的操作。此时可以使用定时锁。
     
    资源的串行访问方法:一个单线程的Executor;使用一个独占锁
     
    可中断的锁获取操作
    lockInterruptibly方法能够在获得锁的同时保持对中断的响应:
           lock.lockInterruptibly();
     
    非块结构的加锁
    内置锁的锁获取和释放都是基于代码块的。类似于ConcurrentHashMap的锁分段技术,在基于散列的容器中实现不同的散列表,以便使用不同的锁。
    连锁式加锁(锁耦合):我们可以采用类似的技术降低链表的锁粒度,即为每一个链表节点使用不同的锁,使不同的线程能独立地对链表的不同部分进行操作。每一个节点的锁将保护链接指针以及在该节点中存储的数据,因此当遍历或修改链表时,必须持有该节点上的这个锁,直到获得了下一个节点的锁。
     
    在synchronized和ReentrantLock之间进行选择
    在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的,可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁。否则,还是应该优先使用synchronized。
     
    内置锁与ReentrantLock相比还有一个优点:在线程转储中能给出在哪些调用帧中获得了锁,并能够检测和识别发生死锁的线程。JVM并不知道哪些线程持有ReentrantLock,因此在调试使用ReentrantLock的线程的问题时,将起不到帮助作用。
     
    未来更可能会提升synchronized而不是ReentrantLock的性能,因为synchronized是JVM的内置属性,它能执行一些优化,例如对线程封闭的锁对象的锁消除优化,通过增加锁的粒度来消除内置锁的同步。
     
    读-写锁(ReadWriteLock)
    ReentrantLock实现了一种标准的互斥锁:每次最多只有一个线程能持有ReentrantLock,但有时候不必要的限制了并发性。
    ReadWriteLock:一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。
    ReadWriteLock接口:
         public interface ReadWriteLock{
                Lock readLock();
                Lock writeLock();
    }。
    在读写锁实现的加锁策略中,允许多个读操作同时进行,但每次只允许一个写操作。ReadWriteLock是一种性能优化,对于在多处理器系统上被频繁读取的数据结构,ReadWriteLock能提高性能。 
    ReentrantReadWriteLock为这两种锁都提供了可重入的加锁语义,ReentrantReadWriteLock中的写入锁只能有唯一的所有者,并且只能由获得该锁的线程来释放。
    当我们只需要一个并发的基于散列的映射,可以使用ConcurrentHashMap;
    如果需要对另一种Map实现(例如LinkedHashMap)提供并发性更高的访问,那么可以使用读-写锁来实现:
    用读-写锁来包装Map:
    public class ReadWriteLock{
       private final Map<k,v> map;
       private final ReadWriteLock lock = new ReentrantReadWriteLock();
       private final Lock r = lock.readLock();
       private final Lock w = lock.writeLock();
       
       public ReadWriteLock(Map<k,v> map){
            this.map = map;
       }
     
       public V put(K key, V value){
            w.lock();
            try{
                   return map.put(key,value);
             }finally{
                  w.unlock(); 
             }
       }
       //对remove(),putAll(),clear()等方法执行相同的操作
     
       public V get(Object key){
             r.lock();
             try{
                  return map.get(key);
              }fianlly{
                     r.unlock();
               }
       }
        //对其他只读的Map方法执行相同的操作
    }
     
  • 相关阅读:
    Jquery源码分析(一)
    Lazyload 延迟加载效果
    20100128
    Linux共享库(so)动态加载和升级
    shell 数组
    Git常用命令解说
    linux shell 数组建立及使用技巧
    linux下查看用户及用户组的方法
    Zypper 用法
    linux shell 数组的长度计算、修改、循环输出等操作
  • 原文地址:https://www.cnblogs.com/xujiming/p/5561800.html
Copyright © 2020-2023  润新知