java中lock与synchronized同步的解析

 现在soa 与分布式计算已经成为互联网公司技术的标配

       那他包含的知识点应该熟悉了解,并以此为基础,去应用,调优各种soa的框架。

       包含如下的四点,是分布式的基础。

        a java 多线程 承接高吞吐量。

        b java nio 承接高并发,与交互协议的定制。

        c java 反射  完成序列化与反序列化。

        d 设计模式的应用 保证应用的扩展性。

 接上篇

     
    因为说锁的原理比较的枯燥,得带着问题场景去说,才能看下去,才能研究下去。

      在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?

  这里主要是要考察说lock与synchronized 的区别。

  1 利用cpu底层机制lock有读锁 与 写锁的区分。

  2 在于上下文的切换与锁的竞争的优化。

  3 关于死锁的避免

  

  Synchronized  只是jvm里面自己的一个协议;

  而关于这个Lock 他的底层里面是有硬件支持的原子操作,各种cpu都支持的,各种平台也支持。如果需要详细理解,可以看看里面的源码,里面有一个重要的类就是AbstractQueuedSynchronizer,  它是轮询处理。


  synchronized 在取不到锁的时候,会休眠一段时间,这样要说开销很大。当然这种synchronized 内部是后面的版本可以进行优化的。

  

1 利用cpu底层机制lock有读锁 与 写锁的区分。

    那实现上面题干的两种方式如下

synchronized例子

代码如下

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class SynchronizedMap<K,V> {  
  2.  private final Map<K,V> map=new HashMap<K, V>();  
  3.    
  4.    
  5.    public synchronized void put(K k,V v){  
  6.     map.put(k, v);  
  7.    }  
  8.    
  9.    public synchronized V get(K k){  
  10.     return map.get(k);  
  11.    }  

这种排斥了 写/写,读/写 读/读。

对于lock,相关代码如下。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class LockMap<K, V> {  
  2.  private final Map<K, V> map=new HashMap<K, V>();  
  3.  private final ReadWriteLock lock=new ReentrantReadWriteLock();  
  4.  private final Lock r=lock.readLock();  
  5.  private final Lock w=lock.writeLock();  
  6.    
  7.    
  8.    
  9.  public void put(K key,V value){  
  10.   w.lock();  
  11.   try {  
  12.    map.put(key, value);  
  13.   } catch (Exception e) {  
  14.    e.printStackTrace();  
  15.   }finally{  
  16.    w.unlock();  
  17.   }  
  18.    
  19.  }  
  20.    
  21.  public V get(K key){  
  22.   r.lock();  
  23.   try {  
  24.    return map.get(key);  
  25.   } catch (Exception e) {  
  26.    e.printStackTrace();  
  27.   }finally{  
  28.    r.unlock();  
  29.   }  
  30.   return null;  
  31.  }  
  32.    
  33.    
  34. }  
这种排斥了 写/写 读/写 。
但读/读没有排斥。

也是就说读与读是多个线程可以同时读的。----可以做为读多写少的应用。


 2在于上下文的切换与锁的竞争的优化。

对于 synchronized 来说。他只有一个条件队列的,里面放着对应于不同类型的(也可以说是处理不同业务类型的)线程,那这时,你只能notifyall
    ,为了保证程序的正确,把所有的线程都叫起来,不管是不是你想要的业务类型的线程。这种对于性能影响是非常大的。比如10个线程在一个条件队列上等待,那么调用notifyAll 将唤醒所有的线程
   这个时候线程产生如下:
       a 它们会在锁上面产生竞争。
       b 它们竞争完了之后大部分又大部分wait了
         这两步,会导致了大量的线程上下文切换。以及大量锁的竞争。

但这个lock是没问题的。他可以对于 不同的条件创建wait-set ,比如生产者消费者模式,生产者生产一个对象,这时想唤醒消费者,只需要在相应的条件上面的wait set进行single.

对于线程安全的lock队列,与线程安全的synchronized stack代码

synchronized 代码如下

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class ProductStack {  
  2.   
  3.     private Product[] products=new Product[10];  
  4.     private int index;  
  5.       
  6.       
  7.     public synchronized void addProduct(Product product){  
  8.         try {  
  9.             while(index>=(products.length-1)){//需要重新检查一下,条件判断s  
  10.                 System.out.println(" the product array is full ; "+Thread.currentThread().getName()+" is waiting");  
  11.                 wait();  
  12.             }  
  13.                   
  14.                 products[index]=product;  
  15.                 index++;  
  16.                 notifyAll();//为了能启动消费线程 当然也唤醒了生产线程。  
  17.         } catch (Exception e) {  
  18.             e.printStackTrace();  
  19.         }  
  20.     }  
  21.       
  22.       
  23.     public synchronized Product pop(){  
  24.         Product product=null;  
  25.         try {  
  26.               
  27.             while(index<=0){ //需要重新检查一下,条件判断  
  28.                 System.out.println("the product array is empty ;"+Thread.currentThread().getName() +"is waiting");  
  29.                 wait();  
  30.             }     
  31.                 index--;   
  32.                 product=products[index];  
  33.                 notifyAll();   //为了能启动 添加线程。 当然也唤醒了消费线程。  
  34.         } catch (Exception e) {  
  35.             e.printStackTrace();  
  36.         }  
  37.           
  38.         return product;  
  39.     }  
  40. }  


对于lock

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. import java.util.concurrent.locks.Condition;  
  2. import java.util.concurrent.locks.Lock;  
  3. import java.util.concurrent.locks.ReentrantLock;  
  4.   
  5. public class ProductQueue<V> {  
  6.     private final static int defaultSize=10;  
  7.     private final V[] queue;  
  8.     private int total;  
  9.     private int tail;  
  10.     private int head;  
  11.     private Lock lock=new ReentrantLock();  
  12.     private Condition notEmpty=lock.newCondition();  
  13.     private Condition notFull= lock.newCondition();  
  14.       
  15.       
  16.     public ProductQueue(){  
  17.           
  18.         this(defaultSize);  
  19.     }  
  20.       
  21.     public ProductQueue(int initialCapacity) {  
  22.         super();  
  23.         this.queue = (V[])new Object[initialCapacity];  
  24.     }  
  25.       
  26.       
  27.     public void push(V v) throws InterruptedException{  
  28.         lock.lock();  
  29.         try {  
  30.             while (isFull()) {  
  31.                 notFull.await();  
  32.             }  
  33.               
  34.             queue[tail] = v;  
  35.             ++tail;  
  36.             if (tail == queue.length)  
  37.                 tail = 0;  
  38.             total++;  
  39.             notEmpty.signal();//唤醒的是同一种类型的线程,不会浪费。  
  40.               
  41.         } finally{  
  42.             lock.unlock();  
  43.         }  
  44.           
  45.     }  
  46.       
  47.       
  48.       
  49.     public V pop() throws InterruptedException{  
  50.         lock.lock();  
  51.         try {  
  52.             while(isEmpty()){  
  53.                 notEmpty.await();  
  54.             }  
  55.             V v=queue[head];  
  56.             head++;  
  57.             if(head==queue.length)head=0;  
  58.             total--;  
  59.             notFull.signal();//唤醒的是同一种类型的线程,不会浪费。  
  60.             return v;  
  61.         } finally{  
  62.             lock.unlock();  
  63.         }  
  64.     }  
  65.       
  66.       
  67.     public boolean isEmpty(){  
  68.         return total==0;  
  69.     }  
  70.     public boolean isFull(){  
  71.         return total==queue.length;  
  72.     }  
  73.       
  74.   
  75. }  

注解里面解释到了问题的根本。

               notifyall 时将所有的线程,生产者,消费者都唤醒了。而此时你只想唤醒生产者,或者只想唤醒消费者,让你胡子眉毛一把抓


3 关于死锁的避免

   产生死锁的本质:至少有两把以上的锁,每个线程获取锁的方式不会一样。实际应用中会有如下3种情况,出现死锁
a  同一类对象
    第一个方法
       synchronized(LockA){
                synchronized(LockB){
                       }
         }
                                  
       第二个方法
              synchronized(LockB){
                   synchronized(LockA){
                             doSomeThing.......
                  }
             }
   以上情况的解决方案是,顺序不一样,把顺序搞一样就成。
  b 对于方法 public void A(SameObject a,SameObject b){
                            synchronized(a){
                                                 synchronized(b){
                                                                                                    doSomeThing.......
                                                        }
                                                            }

                        }
 这里会产生死锁的可能,原因是根据参数的顺序就能有可能被锁了。 这时可以用并发包里面的tryLock最简单         以上是在同一个类里面。
b  这种情况,是在两个类里面,可以想象成为两个资源,
                       在类A里面的有一个a 方法是同步的。
                       在类B里面的有一个b 方法是同步的。
                       a 里面调b方法。
                       b 里面调a方法。
    这里就会产生死锁,因为获取锁的顺序不一样。 这种情况的解决方案是,将方法上的所有的synchronized的都去掉,换成同步块,但同步块同是将传过来的资源,进行一个copy. 这个在并发包里面的有些集合可以参考的。全局,分析锁的个数,获取的顺序。顺序好说,那怎么分析?
怎么去分析死锁呢?
a 争取用同步块,把不能同步方法,从业务角度保证开方式调用。
b 用线程堆栈信息来分析(kill -3)的方式。
c 对于业务的拆解。理论上没有死锁,但是锁管理的资源,在线程处理的时候,占时太长,将业务就要进行重构了。

d 加一功能代码 代码如下     

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ThreadMXBean tmx = ManagementFactory.getThreadMXBean();  
  2.   long[] ids = tmx.findDeadlockedThreads();  
  3.   if (ids != null) {  
  4.      ThreadInfo[] infos = tmx.getThreadInfo(ids, truetrue);  
  5.      System.out.println("The following threads are deadlocked:");  
  6.      for (ThreadInfo ti : infos) {  
  7.         System.out.println(ti);  
  8.      }  
  9.     }  


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值