Java 高并发编程 一道多线程 面试题目

实现一个容器,提供两个方法 add, size
写俩个线程,线程1添加10个元素道容器中,线程2实现监控元素得个数,当个数到5的时候,线程2给出提示并结束。
 

  • 因为涉及到两个线程访问同一个变量,所以应该是变量共享的,会使用到volatile或者synchronized,(只涉及到一个线程去修改资源,另外一个线程去访问,所以这里同步方法可能不大适合)
  • 需要另外一个线程去轮循容器是否已经有5个元素来,这样会很大的浪费资源
  • wait和notify功能,两个线程共同锁定一个对象,通过这个对象,使用wait和notify 使俩个线程来启东和释放锁,这里需要抢到的一点 wait 会释放锁。
public class Container {

    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){
        Container container = new Container();

        final Object o = new Object();

        new Thread(() ->{
            synchronized (o){
                System.out.println("t2 启动");// t2 监控元素
                if (container.size()!=5){
                    try {
                        o.wait();// 释放
                        System.out.println("5个添加够了 t1");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2 结束");
            }
        }, "t2").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()-> {
            System.out.println("t1 启动");// t1 添加元素

            synchronized (o){
                for (int i = 0; i < 10; i++) {
                    container.add(new Object());
                    System.out.println("add: " + i);
                    if (container.size() == 5) {
                        o.notify();
                    }
                }

                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            System.out.println("t1 结束");
            }
        }, "t1").start();
    }
}

执行上面的代码会有个问题:t2 线程并没有在t1线程增加到第5个元素得时候启动,虽然在上面的代码中t2线程有去notify t1线程,但是线程并没有打印出来我们想要的结果,而是在t1结束之后才打印出来。

之所以出现这个问题是因为 notify 并不会去释放锁,所以即使在t1线程中notify t2线程,但是t2线程并没有重新获得锁(在程序刚启动的时候t2  进入 wait 状态,释放了锁)。

解决的方法是,在t1程序 notify t2 得时候需要释放锁,使用的方法就是 让t1也进入wait状态,然后t2程序获得锁,执行结束之后,执行结束之后会释放锁,再notify t1线程运行。

notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行
 * 整个通信过程比较繁琐
 * 
 * 使用Latch(门闩)替代wait notify来进行通知
 * 好处是通信方式简单,同时也可以指定等待时间
 * 使用await和countdown方法替代wait和notify
 * CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行
 * 当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了
 * 这时应该考虑countdownlatch/cyclicbarrier/semaphore

涉及到的方法:CountDownLatch(1),CountDown往下数,当1变为0的时候门闩就开了,latch.countDown()调用一次数就往下-1;latch.await(),门闩的等待是不需要锁定任何对象的;

public class Container {
    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){
        Container container = new Container();
        final Object o = new Object();
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(() ->{
                System.out.println("t2 启动");
                if (container.size()!=5){
                    try {
                        latch.await();
                        // 可以指定等待时间
                        //latch.await(5000, TimeUnit.MILLISECONDS);
                        System.out.println("5个添加够了 t1");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2 结束");
        }, "t2").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()-> {
            System.out.println("t1 启动");

                for (int i = 0; i < 10; i++) {
                    container.add(new Object());
                    System.out.println("add: " + i);
                    if (container.size() == 5) {
                        // 执行 -1 后count 为0,打开门栓,t2 执行。
                        latch.countDown();
                    }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t1 结束");
        }, "t1").start();
    }
}
输出:
t2 启动
t1 启动
add: 0
add: 1
add: 2
add: 3
add: 4
5个添加够了 t1
t2 结束
add: 5
add: 6
add: 7
add: 8
add: 9
t1 结束

原文地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值