首页 > 代码库 > soa---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);
     }
    }





soa---java 多线程的---锁