java基础——多线程(锁lock&&条件阻塞Condition)

来源:互联网 发布:张翔起诉淘宝网 编辑:程序博客网 时间:2024/06/04 18:02

一、Lock实现线程同步通信

1、Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。

[java] view plaincopy
  1. class Output{  
  2.  //1、实例化一把锁,但Lock是个接口,要用ReentrantLock作为实现类  
  3.  Lock lock = new ReentrantLock();  
  4. public void output1(String a){  
  5.     int len = a.length();  
  6.     lock.lock();//2、把所要锁起来的程序加锁  
  7.     try {  
  8.         for (int i = 0; i < len; i++) {  
  9.             System.out.print(a.charAt(i));//打印每个字母  
  10.         }  
  11.     } finally {  
  12.         lock.unlock(); //3、在finally里面打开锁  
  13.     }  
  14.     System.out.println();  
  15. }  

2、读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!

1)下面是jdk配的Demo

[java] view plaincopy
  1. class CachedData {  
  2.    Object data;  
  3.    volatile boolean cacheValid;  
  4.    final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();//读写锁实例  
  5.   
  6.    void processCachedData() {  
  7.      rwl.readLock().lock(); //1、一开始假设有数据,上读锁  
  8.      if (!cacheValid) {//判断是否有数据,有跳2、;没有跳4、  
  9.         // Must release read lock before acquiring write lock  
  10.         rwl.readLock().unlock(); //4、解读锁上写锁  
  11.         rwl.writeLock().lock();  
  12.         try {  
  13.           // Recheck state because another thread might have  
  14.           // acquired write lock and changed state before we did.  
  15.           if (!cacheValid) {  
  16.             data = ...  //5、写数据  
  17.             cacheValid = true;  
  18.           }  
  19.           // Downgrade by acquiring read lock before releasing write lock  
  20.           rwl.readLock().lock(); //6、上读锁,解写锁  
  21.         } finally {  
  22.           rwl.writeLock().unlock(); // Unlock write, still hold read  
  23.         }  
  24.      }  
  25.   
  26.      try {  
  27.        use(data);// 2、有数据,直接读  
  28.      } finally {  
  29.        rwl.readLock().unlock();//3、读完,解锁  
  30.      }  
  31.    }  
  32.  }  
2)面试题:设计一个缓存系统(使用上面Demo的原理)

[java] view plaincopy
  1. public class CacheDemo {  
  2. <span style="white-space:pre">  </span>/** 
  3. <span style="white-space:pre">  </span> * 需求(面试题): 缓存系统(那一个数据,如果缓存有,直接在缓存取数据; 如果没有,缓存就去找数据库,等你下次再找的时候我就可以直接给你) 
  4. <span style="white-space:pre">  </span> */  
  5. <span style="white-space:pre">  </span>// 1、创建一个Map,用于保存数据键值对  
  6. <span style="white-space:pre">  </span>private Map<String, Object> cache = new HashMap<String, Object>();  
  7.   
  8. <span style="white-space:pre">  </span>public static void main(String[] args) {  
  9.   
  10. <span style="white-space:pre">  </span>}  
  11.   
  12. <span style="white-space:pre">  </span>// 定义一把读写锁  
  13. <span style="white-space:pre">  </span>private ReadWriteLock rw1 = new ReentrantReadWriteLock();  
  14.   
  15.   
  16. <span style="white-space:pre">  </span>public Object getData(String key) {  
  17. <span style="white-space:pre">      </span>rw1.readLock().lock();  
  18. <span style="white-space:pre">      </span>// 根据key得到一个Object  
  19. <span style="white-space:pre">      </span>Object value = null;  
  20. <span style="white-space:pre">      </span>try {  
  21. <span style="white-space:pre">          </span>value = cache.get(key);  
  22. <span style="white-space:pre">          </span>if (value == null) {  
  23. <span style="white-space:pre">              </span>rw1.readLock().unlock();  
  24. <span style="white-space:pre">              </span>rw1.writeLock().lock();  
  25. <span style="white-space:pre">              </span>try {  
  26. <span style="white-space:pre">                  </span>if(value == null){ //注意要再次判断value的值,防止多个线程进来了,多次设值  
  27. <span style="white-space:pre">                      </span>value = "bbbbb";  
  28. <span style="white-space:pre">                  </span>}  
  29. <span style="white-space:pre">              </span>} finally {  
  30. <span style="white-space:pre">                  </span>rw1.writeLock().unlock();  
  31. <span style="white-space:pre">              </span>}  
  32. <span style="white-space:pre">              </span>rw1.readLock().lock();  
  33. <span style="white-space:pre">          </span>}  
  34. <span style="white-space:pre">      </span>} finally {  
  35. <span style="white-space:pre">          </span>rw1.readLock().unlock();  
  36. <span style="white-space:pre">      </span>}  
  37. <span style="white-space:pre">      </span>return value;  
  38. <span style="white-space:pre">  </span>}  
  39. }  


二、条件阻塞Condition的应用

1、在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。


2、一个锁内部可以有多个Condition,即有多路等待和通知,可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例,从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。(如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。)

例子-面试题:定义三个线程,一个主线程,两个子线程。主线程循环2次,接着子线程1循环3次,接着子线程2循环4次,接着又回到主线程循环2次,如此循环5次。

[java] view plaincopy
  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 ConditionCommunication {  
  6.     /** 
  7.      *  需求:定义三个线程,一个主线程,两个子线程。主线程循环2次,接着子线程1循环3次,接着子线程2循环4次,接着又回到主线程循环2次,如此循环5次。 
  8.      */  
  9.     public static void main(String[] args) {  
  10.   
  11.         final Business bus = new Business();  
  12.   
  13.   
  14.         new Thread(new Runnable() {  
  15.             @Override  
  16.             public void run() {  
  17.                 for (int i = 1; i <= 4; i++) {  
  18.                     bus.sub1(i);  
  19.                 }  
  20.             }  
  21.         }).start();  
  22.           
  23.         new Thread(new Runnable() {  
  24.             @Override  
  25.             public void run() {  
  26.                 for (int i = 1; i <= 4; i++) {  
  27.                     bus.sub2(i);  
  28.                 }  
  29.             }  
  30.         }).start();  
  31.   
  32.         for (int i = 1; i <= 4; i++) {  
  33.             bus.main(i);  
  34.         }  
  35.     }  
  36.   
  37.     static class Business {  
  38.         private int isExcu = 1//初始值为1,默认先让主线程执行  
  39.         Lock lock = new ReentrantLock();  
  40.         //定义三个Condition便于区分唤醒三个线程  
  41.         Condition conditionMain = lock.newCondition();  
  42.         Condition conditionSub1 = lock.newCondition();  
  43.         Condition conditionSub2 = lock.newCondition();  
  44.   
  45.         public void sub1(int i) {  
  46.             lock.lock();  
  47.             try {  
  48.                 while (isExcu !=2) { //执行值不为2,等待  
  49.                     try {  
  50.                         conditionSub1.await();  
  51.                     } catch (InterruptedException e) {  
  52.                         e.printStackTrace();  
  53.                     }  
  54.                 }  
  55.                 for (int j = 1; j <= 2; j++) {  
  56.                     System.out.println(" 子线程sub1   "  
  57.                             + Thread.currentThread().getName() + "  正在输出    "  
  58.                             + j + "  in loop of  " + i);  
  59.                     isExcu = 3//执行完sub1,赋值给isExcu,让sub2执行  
  60.                     conditionSub2.signal();//唤醒sub2  
  61.                 }  
  62.             } finally {  
  63.                 lock.unlock();  
  64.             }  
  65.         }  
  66.   
  67.         public void sub2(int i) {  
  68.             lock.lock();  
  69.             try {  
  70.                 while (isExcu!=3) {//执行值不为3,本身就等待  
  71.                     try {  
  72.                         conditionSub2.await();  
  73.                     } catch (InterruptedException e) {  
  74.                         e.printStackTrace();  
  75.                     }  
  76.                 }  
  77.                 for (int j = 1; j <= 3; j++) {  
  78.                     System.out.println(" 子线程sub2   "  
  79.                             + Thread.currentThread().getName() + "  正在输出    "  
  80.                             + j + "  in loop of  " + i);  
  81.                     isExcu = 1;//执行完sub2,赋值给isExcu,让main执行  
  82.                     conditionMain.signal();//唤醒main  
  83.                 }  
  84.             } finally {  
  85.                 lock.unlock();  
  86.             }  
  87.   
  88.         }  
  89.   
  90.         public synchronized void main(int i) {  
  91.             lock.lock();  
  92.             try {  
  93.                 while (isExcu!= 1) {//执行值不为1,本身就等待  
  94.                     try {  
  95.                         conditionMain.await();  
  96.                     } catch (InterruptedException e) {  
  97.                         e.printStackTrace();  
  98.                     }  
  99.                 }  
  100.                 for (int k = 1; k <= 1; k++) {  
  101.                     System.out.println(" 主线程   "  
  102.                             + Thread.currentThread().getName() + "  正在输出    "  
  103.                             + k + "  in loop of  " + i);  
  104.                     isExcu = 2;//执行完main,赋值给isExcu,让sub1执行  
  105.                     conditionSub1.signal();//唤醒sub1  
  106.                 }  
  107.             } finally {  
  108.                 lock.unlock();  
  109.             }  
  110.         }  
  111.     }  
  112. }  

打印结果:

 主线程   main  正在输出    1  in loop of  1
 子线程sub1   Thread-0  正在输出    1  in loop of  1
 子线程sub1   Thread-0  正在输出    2  in loop of  1
 子线程sub2   Thread-1  正在输出    1  in loop of  1
 子线程sub2   Thread-1  正在输出    2  in loop of  1
 子线程sub2   Thread-1  正在输出    3  in loop of  1
 主线程   main  正在输出    1  in loop of  2
 子线程sub1   Thread-0  正在输出    1  in loop of  2
 子线程sub1   Thread-0  正在输出    2  in loop of  2
 子线程sub2   Thread-1  正在输出    1  in loop of  2
 子线程sub2   Thread-1  正在输出    2  in loop of  2
 子线程sub2   Thread-1  正在输出    3  in loop of  2
 主线程   main  正在输出    1  in loop of  3
 子线程sub1   Thread-0  正在输出    1  in loop of  3
 子线程sub1   Thread-0  正在输出    2  in loop of  3
 子线程sub2   Thread-1  正在输出    1  in loop of  3
 子线程sub2   Thread-1  正在输出    2  in loop of  3
 子线程sub2   Thread-1  正在输出    3  in loop of  3
 主线程   main  正在输出    1  in loop of  4
 子线程sub1   Thread-0  正在输出    1  in loop of  4
 子线程sub1   Thread-0  正在输出    2  in loop of  4
 子线程sub2   Thread-1  正在输出    1  in loop of  4
 子线程sub2   Thread-1  正在输出    2  in loop of  4
 子线程sub2   Thread-1  正在输出    3  in loop of  4


0 0
原创粉丝点击