【多线程】多线程同步之synchronized和Lock

时间:2022-03-21 18:32:56
1、synchronized同步关键字 
这里涉及到多个线程对共享资源进行处理,这里就必须要给线程“排队”,使用的关键字是synchronized(同步),在程序中,给对共享资源进行处理的代码部分(整个方法或代码块)添加此关键字。相当于给代码块加锁,取得钥匙的线程对资源操作,完成后将钥匙交予其他线程,这保证了同一个资源某一时间内只有一个线程可以对其修改等操作。 
public void func(){ 
    //代码 
    synchronized(锁){ 
         //对共享资源处理代码 
    } 
    //代码 

这里注意: 
      (1)、锁本身必须是共享的资源,不能使局部变量,如在方法内部定义锁,然后使用的话,这样的代码是无意义的,每个对象都会实例化一个锁,锁就失去了作用。比如超市的储物柜钥匙如果每人一把的话,那谁还敢把东西放进去呢。 
public void func(){ 
   Object lock = new Object(); 
    //代码 
    synchronized(lock){//锁失去了意义 
         //对共享资源处理代码 
    } 
    //代码 

     (2)、锁可以是任何一个类型即Object 
     (3)、注意细分锁。可以有多个锁对象,对不同的资源“上锁” 
class A{ 
     private Object lock1 = new Object(); 
     private Object lock2 = new Object(); 
     
     void f(){ 
           synchronized(lock1){ 
                 //访问资源P 
           } 
     } 

     void g(){ 
           synchronized(lock2){ 
                 //访问资源Q 
           } 
     } 



2、Lock 锁对象 
除了synchronized还可以用Lock创建临界区 
class A{ 
     Lock lock = new ReentrantLock(); 
     void f(){ 
            
            lock.lock();//加锁 
              try{ 
                  //访问资源             
            }finally{ 
                  lock.unlock();//解锁 
             } 
     } 



3、synchronized 和 Lock 
(1)Lock的锁定时通过代码实现的,synchronized是在JVM层面上实现的 
(2)synchronized在锁定时如果出现方法块抛出异常,JVM会自动解锁,不会造成因为异常而死锁。 
Lock在出现异常时只能在finally{}将锁释放。 
(3)Lock能完成synchronized所实现的所有功能 
(4)Lock有比synchronized更精确的线程语义和更好的性能。 
(5)在资源竞争不是很激烈的情况下,synchronized的性能优于ReentrantLock,但是当资源竞争很激烈时,synchronized的性能会下降几十倍,而ReentrantLock的性能维持常态。 


二、生产/消费模型 
生产者生产一件产品放到仓库,消费者从仓库取走产品,生产者再生产一件... 
有几种方式呢? 
1、消费者不停的问“有货吗?有货吗?有货吗?”。这无疑是很浪费资源的方式 
2、wait()/notify() 
消费者处于wait(),生产者生产出产品则notify(),然后wait()消费者收到信息进行消费,然后notify生产者,并在此处于wait(),生产者再继续生产... 
生产者类的代码; 
Java代码 
  1. /** 
  2.  * 生产者线程:无数据时再存,存入一个就发通知 
  3.  * @author Administrator 
  4.  * 
  5.  */  
  6. public class ProduceThread extends Thread{  
  7.     //用来标记放入对象的每一个独立ID号  
  8.     private static int count = 0;  
  9.     //与消费线程或共同存取的对象列表  
  10.     private List shareList;  
  11.       
  12.     public ProduceThread(List shareList){  
  13.         this.shareList = shareList;  
  14.     }  
  15.       
  16.     public void run(){  
  17.         System.out.println("生产线程已经启动 。。。"+shareList.size());  
  18.         while(true){  
  19.             try{  
  20.                 Thread.sleep(2000);  
  21.                 synchronized(shareList){  
  22.                     while(shareList.size()>0){  
  23.                         shareList.wait();//有货,等待  
  24.                     }  
  25.                     while(shareList.size()==0){//无货,生产  
  26.                         Student st = new Student();  
  27.                         count++;  
  28.                         st.id = count;  
  29.                         st.name = count+"aaa";  
  30.                         System.out.println("--->生产线程放入对象:"+st.toString());  
  31.                         shareList.add(st);  
  32.                         shareList.notify();//通知  
  33.                     }  
  34.                 }  
  35.             }catch(Exception ef){  
  36.                 ef.printStackTrace();  
  37.             }  
  38.         }  
  39.     }     
  40. }  

消费者类的代码: 
Java代码 
  1. /** 
  2.  * 取数据线程:当有数据时取,取完通知 
  3.  * @author Administrator 
  4.  * 
  5.  */  
  6. public class CustomerThread extends Thread{  
  7.   
  8.     private List shareList;  
  9.       
  10.     public CustomerThread(List shareList){  
  11.         this.shareList = shareList;  
  12.     }  
  13.       
  14.     public void run(){  
  15.         System.out.println("消费线程已经启动。。。"+shareList.size());  
  16.         while(true){  
  17.             try{  
  18.                 synchronized(shareList){  
  19.                     while(shareList.size()==0){  
  20.                         //如果没有,消费线程则等待  
  21.                         shareList.wait();  
  22.                     }  
  23.                     while(shareList.size()>0){  
  24.                         System.out.println("<---消费线程取出 "+shareList.remove(0).toString());  
  25.                         //取了一个已经,立即发出通知  
  26.                         shareList.notify();  
  27.                     }  
  28.                 }  
  29.             }catch(Exception ef){  
  30.                 ef.printStackTrace();  
  31.             }  
  32.         }  
  33.     }  
  34. }  

注意wait()和sleep()的区别是: 
wait()释放锁等待,而sleep()是不释放锁的,即,它在休眠的时候仍然占用资源。 


三、双缓冲模型 
双缓冲模型有两个队列A,B,生产者将产品放到队列A中,消费者从队列B中拿产品,当B队列为空时,将A队列的产品存入B。当B不为空时,消费者只管取货,生产者只管生产,两者之间没有直接的联系,只有B为空时,将B队列同步起来,再将A同步起来,A中产品倒入B。 
这样,就可以大大减少处理同步的次数。