Java中的线程(十)- condition lock

来源:互联网 发布:淘宝ted baker 编辑:程序博客网 时间:2024/05/16 03:45

Lock and Condition

 

Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。
l读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!
l在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
l一个锁内部可以有多个Condition,即有多路等待和通知,可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例,从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。(如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。)

 

 为什么要有读写锁:

提高读的效率,多个读可一起进行.. 读的时候不能进行写操作.,但读的时候可以有其他读操作.

 

-----------------锁的例子----------------------------------
[java] view plaincopy
  1. package thread;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. import java.util.concurrent.locks.Lock;  
  5. import java.util.concurrent.locks.ReentrantLock;  
  6. public class LockTest {  
  7. public static void main(String[] args) {  
  8. final Business business = new Business();  
  9. ExecutorService executor =  Executors.newFixedThreadPool(3);  
  10. for(int i=0;i<3;i++)  
  11. {  
  12. executor.execute(  
  13. new Runnable()  
  14. {  
  15. public void run()  
  16. {  
  17. business.service();  
  18. }  
  19. }  
  20. );  
  21. }  
  22. executor.shutdown();  
  23. }  
  24. private static class Business  
  25. {  
  26. private int count;  
  27. Lock lock = new ReentrantLock();  
  28. public void service()  
  29. {  
  30. lock.lock();  
  31. try {  
  32. count++;  
  33. try {  
  34. Thread.sleep(1);  
  35. catch (InterruptedException e) {  
  36. e.printStackTrace();  
  37. }  
  38. System.out.println(count);  
  39. catch (RuntimeException e) {  
  40. e.printStackTrace();  
  41. }  
  42. finally  
  43. {  
  44. lock.unlock();  
  45. }  
  46. }  
  47. }   
  48. }  

-----------------------读写锁的例子---------------------------
注意:刚开始用eclipse for jee自己的jdk,没有看到读锁可以并发的效果,后来换成sun的jdk,就看到了效果!
 
[java] view plaincopy
  1. import java.util.concurrent.locks.ReentrantReadWriteLock;  
  2. public class ReadWriteLockTest {  
  3. public static void main(String[] args) {  
  4. final Queue3 q3 = new Queue3();  
  5. for(int i=0;i<3;i++)  
  6. {  
  7. new Thread(){  
  8. public void run(){  
  9. while(true){  
  10. q3.get();    
  11. }  
  12. }  
  13. }.start();  
  14. }  
  15. for(int i=0;i<3;i++)  
  16. {   
  17. new Thread(){  
  18. public void run(){  
  19. while(true){  
  20. q3.put(new Random().nextInt(10000));  
  21. }  
  22. }   
  23. }.start();   
  24. }  
  25. }  
  26. }  
  27. class Queue3{  
  28. private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。  
  29. private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();  
  30. public void get(){  
  31. rwl.readLock().lock();  
  32. System.out.println(Thread.currentThread().getName() + " be ready to read data!");  
  33. try {  
  34. Thread.sleep((long)(Math.random()*1000));  
  35. catch (InterruptedException e) {  
  36. e.printStackTrace();  
  37. }  
  38. System.out.println(Thread.currentThread().getName() + “have read data :“ + data);  
  39. rwl.readLock().unlock();  
  40. }  
  41. public void put(Object data){  
  42. rwl.writeLock().lock();  
  43. System.out.println(Thread.currentThread().getName() + " be ready to write data!");   
  44. try {  
  45. Thread.sleep((long)(Math.random()*1000));  
  46. catch (InterruptedException e) {  
  47. e.printStackTrace();  
  48. }  
  49. this.data = data;   
  50. System.out.println(Thread.currentThread().getName() + " have write data: “ + data);  
  51. rwl.writeLock().unlock();   
  52. }  
  53. }  

------------------ Condition的例子1:实现两个线程交替执行-----------------------------
[java] view plaincopy
  1. public class ConditionTest {  
  2. public static void main(String[] args) {  
  3. ExecutorService service = Executors.newSingleThreadExecutor();  
  4. final Business2 business = new Business2();  
  5. service.execute(new Runnable(){  
  6. public void run() {  
  7. for(int i=0;i<50;i++){  
  8. business.sub();  
  9. }  
  10. }  
  11. });  
  12. for(int i=0;i<50;i++){  
  13. business.main();  
  14. }  
  15. }  
  16. }  
  17. class Business2{  
  18. Lock lock = new ReentrantLock();  
  19. Condition condition = lock.newCondition();  
  20. boolean bShouldSub = true;  
  21. public void sub(){  
  22. lock.lock();  
  23. if(!bShouldSub)  
  24. try {  
  25. condition.await();  
  26. catch (InterruptedException e) {  
  27. e.printStackTrace();  
  28. }  
  29. try  
  30. {  
  31. for(int i=0;i<10;i++){  
  32. System.out.println(Thread.currentThread().getName() + " : " + i);  
  33. }  
  34. bShouldSub = false;  
  35. condition.signal();  
  36. }finally{  
  37. lock.unlock();  
  38. }  
  39. }  
  40. public void main(){  
  41. lock.lock();  
  42. if(bShouldSub)  
  43. try {  
  44. condition.await();  
  45. catch (InterruptedException e) {  
  46. // TODO Auto-generated catch block  
  47. e.printStackTrace();  
  48. }   
  49. try  
  50. {  
  51. for(int i=0;i<5;i++){  
  52. System.out.println(Thread.currentThread().getName() + " : " + i);  
  53. }  
  54. bShouldSub = true;  
  55. condition.signal();   
  56. }finally{  
  57. lock.unlock();  
  58. }   
  59. }  
  60. }  

--------- Condition的例子2:实现三个线程交替运行的效果--------------------------
[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. public class SignalTest2 {  
  5. public static void main(String[] args) {  
  6. new SignalTest2().init();  
  7. }  
  8. private void init(){  
  9. final Business b = new Business();  
  10. new Thread(){  
  11. public void run(){  
  12. for(int i=0;i<50;i++)  
  13. b.main();  
  14. }  
  15. }.start();  
  16. new Thread(){  
  17. public void run(){  
  18. for(int i=0;i<50;i++)    
  19. b.sub();  
  20. }   
  21. }.start();  
  22. new Thread(){  
  23. public void run(){  
  24. for(int i=0;i<50;i++)    
  25. b.sub2();  
  26. }   
  27. }.start();   
  28. }  
  29. private class Business{  
  30. int status = 1;  
  31. Lock lock = new ReentrantLock();  
  32. Condition cond1 = lock.newCondition();  
  33. Condition cond2 = lock.newCondition();  
  34. Condition cond3 = lock.newCondition();   
  35. public  void main(){  
  36. lock.lock();  
  37. while(status != 1){  
  38. try{cond1.await();}catch(Exception e){}  
  39. }  
  40. for(int i=1;i<=5;i++){  
  41.   try{Thread.sleep(200);}catch(Exception e){}  
  42.   System.out.println(Thread.currentThread().getName() + ":" + i);  
  43. }  
  44. status = 2;  
  45. cond2.signal();  
  46. lock.unlock();  
  47. }  
  48. public  void sub(){  
  49. lock.lock();   
  50. while(status != 2){  
  51. try{cond2.await();}catch(Exception e){}  
  52. }  
  53. for(int i=1;i<=10;i++){  
  54.   try{Thread.sleep(200);}catch(Exception e){}  
  55.   System.out.println(Thread.currentThread().getName() + ":" + i);  
  56. }  
  57. status = 3;  
  58. cond3.signal();  
  59. lock.unlock();  
  60. }  
  61. public  void sub2(){  
  62. lock.lock();   
  63. while(status != 3){  
  64. try{cond3.await();}catch(Exception e){}  
  65. }  
  66. for(int i=1;i<=10;i++){  
  67.   try{Thread.sleep(200);}catch(Exception e){}  
  68.   System.out.println(Thread.currentThread().getName() + ":" + i);  
  69. }  
  70. status = 1;  
  71. cond1.signal();  
  72. lock.unlock();  
  73. }   
  74. }  
  75. }  

 
Semaphore
Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。

 

[java] view plaincopy
  1. package cn.itcast.thread;  
  2.   
  3. import java.util.concurrent.ExecutorService;  
  4. import java.util.concurrent.Executors;  
  5. import java.util.concurrent.Semaphore;  
  6.   
  7. public class SemaphoreTest {  
  8.  public static void main(String[] args) {  
  9.   ExecutorService service = Executors.newCachedThreadPool();  
  10.   final  Semaphore sp = new Semaphore(3);  
  11.   for(int i=0;i<10;i++){  
  12.    Runnable runnable = new Runnable(){  
  13.      public void run(){  
  14.      try {  
  15.       sp.acquire();  
  16.      } catch (InterruptedException e1) {  
  17.       e1.printStackTrace();  
  18.      }  
  19.      System.out.println("线程" + Thread.currentThread().getName() +   
  20.        "进入,当前已有" + (3-sp.availablePermits()) + "个并发");  
  21.      try {  
  22.       Thread.sleep((long)(Math.random()*10000));  
  23.      } catch (InterruptedException e) {  
  24.       e.printStackTrace();  
  25.      }  
  26.      System.out.println("线程" + Thread.currentThread().getName() +   
  27.        "即将离开");       
  28.      sp.release();  
  29.      //下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元  
  30.      System.out.println("线程" + Thread.currentThread().getName() +   
  31.        "已离开,当前已有" + (3-sp.availablePermits()) + "个并发");       
  32.     }  
  33.    };  
  34.    service.execute(runnable);     
  35.   }  
  36.   
  37.  }  
  38.   
  39. }  



 

CyclicBarrier
Ø表示大家彼此等待,大家集合好后才开始出发,分散活动后又在指定地点集合碰面,这就好比整个公司的人员利用周末时间集体郊游一样,先各自从家出发到公司集合后,再同时出发到公园游玩,在指定地点集合后再同时开始就餐,…。
lCountDownLatch
Ø犹如倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。这直接通过代码来说明CountDownLatch的作用,这样学员的理解效果更直接。
Ø可以实现一个人(也可以是多个人)等待其他所有人都来通知他,这犹如一个计划需要多个领导都签字后才能继续向下实施。还可以实现一个人通知多个人的效果,类似裁判一声口令,运动员同时开始奔跑。用这个功能做百米赛跑的游戏程序不错哦!
lExchanger
Ø用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据。

 

 

-----------------CyclicBarrier的代码:---------------------------------
[java] view plaincopy
  1. package cn.itcast.day3.thread;  
  2. import java.util.concurrent.CyclicBarrier;  
  3. import java.util.concurrent.ExecutorService;  
  4. import java.util.concurrent.Executors;  
  5. import java.util.concurrent.Semaphore;  
  6. public class CyclicBarrierTest {  
  7. public static void main(String[] args) {  
  8. ExecutorService service = Executors.newCachedThreadPool();  
  9. final  CyclicBarrier cb = new CyclicBarrier(3);  
  10. for(int i=0;i<3;i++){  
  11. Runnable runnable = new Runnable(){  
  12. public void run(){  
  13. try {  
  14.   Thread.sleep((long)(Math.random()*10000));   
  15. System.out.println("线程" + Thread.currentThread().getName() +   
  16. "即将到达集合地点1,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");    
  17. cb.await();  
  18.   Thread.sleep((long)(Math.random()*10000));   
  19. System.out.println("线程" + Thread.currentThread().getName() +   
  20. "即将到达集合地点2,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");    
  21. cb.await();   
  22.   Thread.sleep((long)(Math.random()*10000));   
  23. System.out.println("线程" + Thread.currentThread().getName() +   
  24. "即将到达集合地点3,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");    
  25. cb.await();    
  26. catch (Exception e) {  
  27. e.printStackTrace();  
  28. }    
  29. }  
  30. };  
  31. service.execute(runnable);  
  32. }  
  33. service.shutdown();  
  34. }  
  35. }  

-----------------CountdownLatch的代码:---------------------------------
 
[java] view plaincopy
  1. package cn.itcast.day3.thread;  
  2. import java.util.concurrent.CountDownLatch;  
  3. import java.util.concurrent.CyclicBarrier;  
  4. import java.util.concurrent.ExecutorService;  
  5. import java.util.concurrent.Executors;  
  6. public class CountdownLatchTest {  
  7. public static void main(String[] args) {  
  8. ExecutorService service = Executors.newCachedThreadPool();  
  9. final CountDownLatch cdOrder = new CountDownLatch(1);  
  10. final CountDownLatch cdAnswer = new CountDownLatch(3);   
  11. for(int i=0;i<3;i++){  
  12. Runnable runnable = new Runnable(){  
  13. public void run(){  
  14. try {  
  15. System.out.println("线程" + Thread.currentThread().getName() +   
  16. "正准备接受命令");   
  17. cdOrder.await();  
  18. System.out.println("线程" + Thread.currentThread().getName() +   
  19. "已接受命令");    
  20.   Thread.sleep((long)(Math.random()*10000));   
  21. System.out.println("线程" + Thread.currentThread().getName() +   
  22. "回应命令处理结果");   
  23. cdAnswer.countDown();    
  24. catch (Exception e) {  
  25. e.printStackTrace();  
  26. }    
  27. }  
  28. };  
  29. service.execute(runnable);  
  30. }   
  31. try {  
  32. Thread.sleep((long)(Math.random()*10000));  
  33. System.out.println("线程" + Thread.currentThread().getName() +   
  34. "即将发布命令");    
  35. cdOrder.countDown();  
  36. System.out.println("线程" + Thread.currentThread().getName() +   
  37. "已发送命令,正在等待结果");   
  38. cdAnswer.await();  
  39. System.out.println("线程" + Thread.currentThread().getName() +   
  40. "已收到所有响应结果");   
  41. catch (Exception e) {  
  42. e.printStackTrace();  
  43. }   
  44. service.shutdown();  
  45. }  
  46. }  

---------------------------ExchangerTest-------------------------
讲解Exchanger的比喻:好比两个毒贩要进行交易,一手交钱、一手交货,不管谁先来到接头地点后,就处于等待状态了,当另外一方也到达了接头地点(所谓到达接头地点,也就是到到达了准备接头的状态)时,两者的数据就立即交换了,然后就又可以各忙各的了。
exchange方法就相当于两手高高举着待交换物,等待人家前来交换,一旦人家到来(即人家也执行到exchange方法),则两者立马完成数据的交换。
 
[java] view plaincopy
  1. package cn.itcast.day3.thread;  
  2. import java.util.concurrent.Exchanger;  
  3. import java.util.concurrent.ExecutorService;  
  4. import java.util.concurrent.Executors;  
  5. public class ExchangerTest {  
  6. public static void main(String[] args) {  
  7. ExecutorService service = Executors.newCachedThreadPool();  
  8. final Exchanger exchanger = new Exchanger();  
  9. service.execute(new Runnable(){  
  10. public void run() {  
  11. try {   
  12. Thread.sleep((long)(Math.random()*10000));  
  13. String data1 = "zxx";  
  14. System.out.println("线程" + Thread.currentThread().getName() +   
  15. "正在把数据" + data1 +"换出去");  
  16. String data2 = (String)exchanger.exchange(data1);  
  17. System.out.println("线程" + Thread.currentThread().getName() +   
  18. "换回的数据为" + data2);  
  19. }catch(Exception e){  
  20. }  
  21. }   
  22. });  
  23. service.execute(new Runnable(){  
  24. public void run() {  
  25. try {   
  26. Thread.sleep((long)(Math.random()*10000));  
  27. String data1 = "lhm";  
  28. System.out.println("线程" + Thread.currentThread().getName() +   
  29. "正在把数据" + data1 +"换出去");  
  30. String data2 = (String)exchanger.exchange(data1);  
  31. System.out.println("线程" + Thread.currentThread().getName() +   
  32. "换回的数据为" + data2);  
  33. }catch(Exception e){  
  34. }   
  35. }   
  36. });   
  37. }  
  38. }  

 
Java5中提供了如下一些同步集合类:
Ø通过看java.util.concurrent包下的介绍可以知道有哪些并发集合
ØConcurrentHashMap
ØCopyOnWriteArrayList
ØCopyOnWriteArraySet
原创粉丝点击