zoukankan      html  css  js  c++  java
  • java高并发编程(二)

    马士兵java并发编程的代码,照抄过来,做个记录。

    一、分析下面面试题

    /**
     * 曾经的面试题:(淘宝?)
     * 实现一个容器,提供两个方法,add,size
     * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
     * 
     * 分析下面这个程序,能完成这个功能吗?
     * @author mashibing
     */
    package yxxy.c_019;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    
    public class MyContainer1 {
    
        List lists = new ArrayList();
    
        public void add(Object o) {
            lists.add(o);
        }
    
        public int size() {
            return lists.size();
        }
        
        public static void main(String[] args) {
            MyContainer1 c = new MyContainer1();
    
            new Thread(() -> {
                for(int i=0; i<10; i++) {
                    c.add(new Object());
                    System.out.println("add " + i);
                    
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "t1").start();
            
            new Thread(() -> {
                while(true) {
                    if(c.size() == 5) {
                        break;
                    }
                }
                System.out.println("t2 结束");
            }, "t2").start();
        }
    }
    分析:
    不能完成这个功能;
    添加volatile关键字,修改为如下:
     
    二、添加volatile:
    /**
     * 曾经的面试题:(淘宝?)
     * 实现一个容器,提供两个方法,add,size
     * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
     * 
     * 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?
     * @author mashibing
     */
    package yxxy.c_019;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    
    public class MyContainer2 {
    
        //添加volatile,使t2能够得到通知
        volatile List lists = new ArrayList();
    
        public void add(Object o) {
            lists.add(o);
        }
    
        public int size() {
            return lists.size();
        }
        
        public static void main(String[] args) {
            MyContainer2 c = new MyContainer2();
    
            new Thread(() -> {
                for(int i=0; i<10; i++) {
                    c.add(new Object());
                    System.out.println("add " + i);
                    
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "t1").start();
            
            new Thread(() -> {
                while(true) {
                    if(c.size() == 5) {
                        break;
                    }
                }
                System.out.println("t2 结束");
            }, "t2").start();
        }
    }

    但是上面代码还存在两个问题:

    1)由于没加同步,c.size()等于5的时候,假如另外一个线程又往上增加了1个,实际上这时候已经等于6了才break,所以不是很精确;

    2)浪费CPU,t2线程的死循环很浪费cpu

    三、使用wait和notify

    /**
     * 曾经的面试题:(淘宝?)
     * 实现一个容器,提供两个方法,add,size
     * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
     * 
     * 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?
     * 
     * 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁
     * 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以
     * 
     * 阅读下面的程序,并分析输出结果
     * 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出
     * 想想这是为什么?
     * @author mashibing
     */
    package yxxy.c_019;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    
    public class MyContainer3 {
    
        //添加volatile,使t2能够得到通知
        volatile List lists = new ArrayList();
    
        public void add(Object o) {
            lists.add(o);
        }
    
        public int size() {
            return lists.size();
        }
        
        public static void main(String[] args) {
            MyContainer3 c = new MyContainer3();
            
            final Object lock = new Object();
            
            new Thread(() -> {
                synchronized(lock) {
                    System.out.println("t2启动");
                    if(c.size() != 5) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("t2 结束");
                }
                
            }, "t2").start();
            
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
    
            new Thread(() -> {
                System.out.println("t1启动");
                synchronized(lock) {
                    for(int i=0; i<10; i++) {
                        c.add(new Object());
                        System.out.println("add " + i);
                        
                        if(c.size() == 5) {
                            lock.notify();
                        }
                        
                        try {
                            TimeUnit.SECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, "t1").start();
            
            
        }
    }

    分析:

    1)解释wait和notify方法:

    使用wait和notify必须进行锁定,如果没有锁定这个对象,你就不能调这个对象的wait和notify方法;
    wait和notify是调用被锁定对象的wait和notify方法。
    比如有一个对象A,两个线程1,2来访问这个对象A,第一个线程1的访问过程中假如条件没有满足,想让这个线程1暂停,等着;这时候怎么办的呢?
    首先线程1要锁定对象A,然后调用这个对象A的wait方法,线程1就进入等待状态,同时释放锁,别的线程可以进来;
     
    什么时候线程1会再次启动呢?只有再调用这个对象A的notify方法。notify方法会启动一个正在这个对象A上等待的某一个线程,或者是调用notifyAll(),
    notifyAll方法是叫醒在这个对象上正在等待的所有线程。
     
    notify无法指定的通知启动哪一个具体的线程,没法指定,由cpu线程调度器自己帮你找一个线程运行。
     
     
     
    2)为什么size=5了,t2线程没有结束?
    由于notify不会释放锁,即便你通知了t2,让它起来了,它起来之后想往下运行,wait了之后想重新继续往下运行是需要重新得到lock这把锁的,可是很不幸的是t1已经把这个锁锁定了,所以只有等t1执行完了,t2才会继续执行。

    四、

    /**
     * 曾经的面试题:(淘宝?)
     * 实现一个容器,提供两个方法,add,size
     * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
     * 
     * 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?
     * 
     * 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁
     * 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以
     * 
     * 阅读下面的程序,并分析输出结果
     * 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出
     * 想想这是为什么?
     * 
     * notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行
     * 整个通信过程比较繁琐
     * @author mashibing
     */
    package yxxy.c_019;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    
    public class MyContainer4 {
    
        //添加volatile,使t2能够得到通知
        volatile List lists = new ArrayList();
    
        public void add(Object o) {
            lists.add(o);
        }
    
        public int size() {
            return lists.size();
        }
        
        public static void main(String[] args) {
            MyContainer4 c = new MyContainer4();
            
            final Object lock = new Object();
            
            new Thread(() -> {
                synchronized(lock) {
                    System.out.println("t2启动");
                    if(c.size() != 5) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("t2 结束");
                    //通知t1继续执行
                    lock.notify();
                }
                
            }, "t2").start();
            
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
    
            new Thread(() -> {
                System.out.println("t1启动");
                synchronized(lock) {
                    for(int i=0; i<10; i++) {
                        c.add(new Object());
                        System.out.println("add " + i);
                        
                        if(c.size() == 5) {
                            lock.notify();
                            //释放锁,让t2得以执行
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        
                        try {
                            TimeUnit.SECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, "t1").start();
            
            
        }
    }

    流程图:

    上面的通信过程太繁琐了,有没有简单点的办法?

    五、

    /**
     * 曾经的面试题:(淘宝?)
     * 实现一个容器,提供两个方法,add,size
     * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
     * 
     * 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?
     * 
     * 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁
     * 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以
     * 
     * 阅读下面的程序,并分析输出结果
     * 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出
     * 想想这是为什么?
     * 
     * notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行
     * 整个通信过程比较繁琐
     * 
     * 使用Latch(门闩)替代wait notify来进行通知
     * 好处是通信方式简单,同时也可以指定等待时间
     * 使用await和countdown方法替代wait和notify
     * CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行
     * 当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了
     * 这时应该考虑countdownlatch/cyclicbarrier/semaphore
     * @author mashibing
     */
    package yxxy.c_019;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    
    public class MyContainer5 {
    
        // 添加volatile,使t2能够得到通知
        volatile List lists = new ArrayList();
    
        public void add(Object o) {
            lists.add(o);
        }
    
        public int size() {
            return lists.size();
        }
    
        public static void main(String[] args) {
            MyContainer5 c = new MyContainer5();
    
            CountDownLatch latch = new CountDownLatch(1);
    
            new Thread(() -> {
                System.out.println("t2启动");
                if (c.size() != 5) {
                    try {
                        latch.await();
                        
                        //也可以指定等待时间
                        //latch.await(5000, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2 结束");
    
            }, "t2").start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
    
            new Thread(() -> {
                System.out.println("t1启动");
                for (int i = 0; i < 10; i++) {
                    c.add(new Object());
                    System.out.println("add " + i);
    
                    if (c.size() == 5) {
                        // 打开门闩,让t2得以执行
                        latch.countDown();
                    }
    
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
            }, "t1").start();
    
        }
    }
     
    CountDownLatch(1),CountDown往下数,当1变为0的时候门闩就开了,latch.countDown()调用一次数就往下-1;
    latch.await(),门闩的等待是不需要锁定任何对象的;
  • 相关阅读:
    87后的我
    meta标签中的httpequiv属性
    System.IO.Directory.GetCurrentDirectory与System.Windows.Forms.Application.StartupPath的用法
    递归方法重现
    实现控件的随意拖动
    Sql Server 2005 如何自动备份数据库
    用C#打开Windows自带的图片传真查看器
    配置SQL Server2005以允许远程访问
    Gridview控件有关的一些设置
    Add the Child Pane to Root.plist in the setting.bundlle
  • 原文地址:https://www.cnblogs.com/tenWood/p/9344817.html
Copyright © 2011-2022 走看看