博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java高并发编程(二)
阅读量:6985 次
发布时间:2019-06-27

本文共 9898 字,大约阅读时间需要 32 分钟。

马士兵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(),门闩的等待是不需要锁定任何对象的;

 

转载于:https://www.cnblogs.com/tenWood/p/9344817.html

你可能感兴趣的文章
vim 使用
查看>>
为敏感信息设置安全屏障
查看>>
进程与线程
查看>>
经典SQL
查看>>
维基百科:主流移动设备的屏幕参数
查看>>
使用CGContext画线操作小记
查看>>
mysql fabric安装使用测试
查看>>
java 对 mongoDB 分组统计操作 以及一些常用操作
查看>>
当你扛不住的时候就读读
查看>>
解决安装rrdtool遇到的一个问题
查看>>
linux启动过程
查看>>
QString与LPCWSTR互转
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
xmlUtil 解析 创建
查看>>
我的友情链接
查看>>
linux 命令(3)echo
查看>>
Nginx基础入门之nginx基础配置项介绍(2)
查看>>
一次详细全面的***报告
查看>>
c# 三种异步编程模型EAP(*)、 APM(*)和 TPL
查看>>