zoukankan      html  css  js  c++  java
  • soa---java 多线程的---锁

           如今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样例

    代码例如以下

     

    public class SynchronizedMap<K,V> {
     private final Map<K,V> map=new HashMap<K, V>();
     
     
       public synchronized void put(K k,V v){
        map.put(k, v);
       }
     
       public synchronized V get(K k){
        return map.get(k);
       }
    

    这样的排斥了 写/写,读/写 读/读。

    对于lock,相关代码例如以下。

    public class LockMap<K, V> {
     private final Map<K, V> map=new HashMap<K, V>();
     private final ReadWriteLock lock=new ReentrantReadWriteLock();
     private final Lock r=lock.readLock();
     private final Lock w=lock.writeLock();
     
     
     
     public void put(K key,V value){
      w.lock();
      try {
       map.put(key, value);
      } catch (Exception e) {
       e.printStackTrace();
      }finally{
       w.unlock();
      }
     
     }
     
     public V get(K key){
      r.lock();
      try {
       return map.get(key);
      } catch (Exception e) {
       e.printStackTrace();
      }finally{
       r.unlock();
      }
      return null;
     }
     
     
    }
    
    
    这样的排斥了 写/写 读/写 。


    但读/读没有排斥。


    也是就说读与读是多个线程能够同一时候读的。----能够做为读多写少的应用。


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

    对于 synchronized 来说。

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

    以及大量锁的竞争。

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

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

    synchronized 代码例如以下

    public class ProductStack {
    
    	private Product[] products=new Product[10];
    	private int index;
    	
    	
    	public synchronized void addProduct(Product product){
    		try {
    			while(index>=(products.length-1)){//须要又一次检查一下。条件推断s
    				System.out.println(" the product array is full ; "+Thread.currentThread().getName()+" is waiting");
    				wait();
    			}
    				
    				products[index]=product;
    				index++;
    				notifyAll();//为了能启动消费线程 当然也唤醒了生产线程。
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	
    	
    	public synchronized Product pop(){
    		Product product=null;
    		try {
    			
    			while(index<=0){ //须要又一次检查一下。条件推断
    				System.out.println("the product array is empty ;"+Thread.currentThread().getName() +"is waiting");
    				wait();
    			}   
    			    index--; 
    			    product=products[index];
    				notifyAll();   //为了能启动 加入线程。 当然也唤醒了消费线程。
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		
    		return product;
    	}
    }
    


    对于lock

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ProductQueue<V> {
    	private final static int defaultSize=10;
    	private final V[] queue;
    	private int total;
    	private int tail;
    	private int head;
    	private Lock lock=new ReentrantLock();
    	private Condition notEmpty=lock.newCondition();
    	private Condition notFull= lock.newCondition();
    	
    	
    	public ProductQueue(){
    		
    		this(defaultSize);
    	}
    	
    	public ProductQueue(int initialCapacity) {
    		super();
    		this.queue = (V[])new Object[initialCapacity];
    	}
    	
    	
    	public void push(V v) throws InterruptedException{
    		lock.lock();
    		try {
    			while (isFull()) {
    				notFull.await();
    			}
    			
    			queue[tail] = v;
    			++tail;
    			if (tail == queue.length)
    				tail = 0;
    			total++;
    			notEmpty.signal();//唤醒的是同一种类型的线程,不会浪费。
    			
    		} finally{
    			lock.unlock();
    		}
    		
    	}
    	
    	
    	
    	public V pop() throws InterruptedException{
    		lock.lock();
    		try {
    			while(isEmpty()){
    				notEmpty.await();
    			}
    			V v=queue[head];
    			head++;
    			if(head==queue.length)head=0;
    			total--;
    			notFull.signal();//唤醒的是同一种类型的线程,不会浪费。
    			return v;
    		} finally{
    			lock.unlock();
    		}
    	}
    	
    	
    	public boolean isEmpty(){
    		return total==0;
    	}
    	public boolean isFull(){
    		return total==queue.length;
    	}
      	
    
    }
    

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

                   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 加一功能代码 代码例如以下    

    ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
      long[] ids = tmx.findDeadlockedThreads();
      if (ids != null) {
         ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true);
         System.out.println("The following threads are deadlocked:");
         for (ThreadInfo ti : infos) {
            System.out.println(ti);
         }
        }





  • 相关阅读:
    python学习之argparse模块的使用
    pip pytorch安装时出现的问题
    ubuntu16.04中将python3设置为默认
    ubuntu复制文件或目录
    如何验证电脑的网卡是否可以正常使用
    《机器学习》西瓜书第六章支持向量机
    《机器学习》西瓜书第五章神经网络
    《机器学习》西瓜书第四章决策树
    《机器学习》西瓜书第三章线性模型笔记
    JavaScript笔记
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5368713.html
Copyright © 2011-2022 走看看