• Java并发编程_wait/notify和CountDownLatch的比较(三)


     1、wait/notify方法

    package sync;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class WaitAndNotify {
    
    	private volatile static List list= new ArrayList();
    	
    	private void add() {
    		list.add("wang");
    	}
    	
    	private int size() {
    		return list.size();
    	}
    	
    	
    	public static void main(String[] args) {
    		
    		final WaitAndNotify list1 = new WaitAndNotify();
    		/*
    		 * 1. 实例化一个Object对象当作锁,下面t1和t2竞争这把锁实现同步
    		 * 2. Object下面会有wait、notify方法,(面试经常问:Object下面有哪些方法,)
    		 * 3. 说明每个对象都有这两个方法,都可以当作锁
    		 */
    		final Object lock = new Object();
    		
    		Thread t1 = new Thread(new Runnable() {
    			@Override
    			public void run() {
    				try {
    					synchronized (lock) {
    						for(int i = 0; i < 10; i++) {
    							list1.add();
    							System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素");
    							Thread.sleep(500);
    							if(list1.size() == 5) {
    								System.out.println("已经发出通知..");
    								//notify不释放锁,for循环还会继续执行
    								lock.notify();
    							}
    						}
    					}
    					
    				} catch(InterruptedException e) {
    					e.printStackTrace();
    				}
    				
    				
    			}
    		}, "t1");
    		
    		Thread t2 = new Thread(new Runnable() {
    			@Override
    			public void run() {
    				synchronized(lock) {
    					/*
    					 * 如果先执行t2线程,如果size不是5,就wait
    					 * wait会释放锁
    					 */
    					if(list1.size() != 5) {
    						try {
    							System.out.println("t2进入..");
    							Thread.sleep(3000);
    							lock.wait();
    						}catch(InterruptedException e) {
    							e.printStackTrace();
    						}
    					}
    					//
    					System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + "list size = 5,线程停止");
    					throw new RuntimeException();
    				}
    				
    			}
    		}, "t2");
    		
    		t2.start();
    		t1.start();
    	}
    }

    输出结果:

    t2进入..
    当前线程:t1添加了一个元素
    当前线程:t1添加了一个元素
    当前线程:t1添加了一个元素
    当前线程:t1添加了一个元素
    当前线程:t1添加了一个元素
    已经发出通知..
    当前线程:t1添加了一个元素
    当前线程:t1添加了一个元素
    当前线程:t1添加了一个元素
    当前线程:t1添加了一个元素
    当前线程:t1添加了一个元素
    当前线程收到通知:t2list size = 5,线程停止
    Exception in thread "t2" java.lang.RuntimeException
    	at sync.WaitAndNotify$2.run(WaitAndNotify.java:73)
    	at java.lang.Thread.run(Unknown Source)
    

      

    代码解读:

    第一步:执行t2线程,进入run方法,list.size不等于5,就lock.wait释放锁,t2进程等待,转而执行t1

    第二步:执行t1进程,得到锁,执行for循环,当list.size等于5时,发出通知..唤醒t2进程,但是会继续执行完for循环,因为notify不释放锁

    第三步:t2进程被唤醒,因此list.size已经等于10,不等于5,直接输出最后两行代码

    wait/notify的方式弊端:

    t2线程先start,因为其List的size!=5,所以执行lock.wait()释放对象锁,这样在t1线程就可以获得这把lock对象锁。

    t1线程向List中添加元素,当List的size==5时,执行lock.notify(),发出唤醒通知,此时t1线程并不释放lock对象锁,所以这时t2虽然收到唤醒的通知,但是由于t1此时并未释放lock对象锁,所以t2只能一直等待,直到t1执行完毕释放lock对象锁,t2才能获取到lock对象锁,执行lock.wait();后面的代码。

     2、CountDownLatch方法

    下面使用java.util.concurrent包下的类CountDownLatch对上面wait/notify方式的代码进行改造。

    CountDownLatch机制不是用来保护共享资源或临界区,而是用来同步一个或多个执行任务的线程。

    import java.util.ArrayList;  
    import java.util.List;  
    import java.util.Queue;  
    import java.util.concurrent.CountDownLatch;  
    import java.util.concurrent.LinkedBlockingDeque;  
    import java.util.concurrent.LinkedBlockingQueue;  
    /** 
     * wait notfiy 方法,wait释放锁,notfiy不释放锁 
     * 
     */  
    public class ListAdd2 {  
        private volatile static List list = new ArrayList();      
          
        public void add(){  
            list.add("abc");  
        }  
        public int size(){  
            return list.size();  
        }  
          
        public static void main(String[] args) {  
              
            final ListAdd2 list2 = new ListAdd2();  
              
            // 当使用wait 和 notify 的时候 , 一定要配合着synchronized关键字去使用  
            //final Object lock = new Object();  
              
            final CountDownLatch countDownLatch = new CountDownLatch(1);  
              
            Thread t1 = new Thread(new Runnable() {  
                @Override  
                public void run() {  
                    try {  
                        //synchronized (lock) {  
                            for(int i = 0; i <10; i++){  
                                list2.add();  
                                System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");  
                                Thread.sleep(500);  
                                if(list2.size() == 5){  
                                    System.out.println("已经发出通知..");  
                                    countDownLatch.countDown();  
                                    //lock.notify();//不释放对象锁  
                                }  
                            }                         
                        //}  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
      
                }  
            }, "t1");  
              
            Thread t2 = new Thread(new Runnable() {  
                @Override  
                public void run() {  
                    //synchronized (lock) {  
                        if(list2.size() != 5){  
                            try {  
                                //System.out.println("t2进入...");  
                                //lock.wait();//释放对象锁  
                                countDownLatch.await();  
                            } catch (InterruptedException e) {  
                                e.printStackTrace();  
                            }  
                        }  
                        System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");  
                        throw new RuntimeException();  
                    //}  
                }  
            }, "t2");     
              
            t2.start();//t2先启动  
            t1.start();  
              
        }  
          
    }  

    t2先启动,countDownLatch.await()方法进行阻塞。

    t1启动后再运行过程中,当List的size==5时,执行countDownLatch.countDown()发出唤醒通知,

    此时,t2接收到通知后,由于没有使用synchronized关键字涉及不到获取锁的问题,因此t2收到通知立即开始执行countDownLatch.await()后面的代码。

    在Eclipse中console输出内容如下:

  • 相关阅读:
    053-242
    053-227
    053-671
    053-489
    053-670
    sql
    白纸黑字签字画押,出人命的事
    CSS
    JS
    Eclipse发布地址不同引发的问题
  • 原文地址:https://www.cnblogs.com/Donnnnnn/p/9074552.html
Copyright © 2020-2023  润新知