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(),门闩的等待是不需要锁定任何对象的;
  • 相关阅读:
    0309. Best Time to Buy and Sell Stock with Cooldown (M)
    0621. Task Scheduler (M)
    0106. Construct Binary Tree from Inorder and Postorder Traversal (M)
    0258. Add Digits (E)
    0154. Find Minimum in Rotated Sorted Array II (H)
    0797. All Paths From Source to Target (M)
    0260. Single Number III (M)
    0072. Edit Distance (H)
    0103. Binary Tree Zigzag Level Order Traversal (M)
    0312. Burst Balloons (H)
  • 原文地址:https://www.cnblogs.com/tenWood/p/9344817.html
Copyright © 2011-2022 走看看