码迷,mamicode.com
首页 > 编程语言 > 详细

一道多线程面试题的多种解决方法

时间:2020-06-20 16:56:30      阅读:72      评论:0      收藏:0      [点我收藏+]

标签:cut   循环   int   消费   rac   进入   tac   bottom   ++   

一道多线程面试题的多种解决方法

题目

实现一个容器,线程2添加10个元素,线程1实时监督线程2,当容器中元素个数达到5个时,给出提示并结束。

分析

这题本身并不是很难,类似生产者、消费者问题,线程1等待线程2添加元素达到5个,线程1消费这个事件。
因此可以想到的是利用生产者消费者范式来解决。
我主要用生产者消费者范式和JUC中各个常用组件来解决这个问题,熟悉下各个组件使用方法。

整体测试设计

为了便于测试,这里建立了一个T1_Supervice_T2抽象类,提供两个模板方法,子类实现并用作t1, t2 线程的运行方法。T1_Supervice_T2_Test用来测试各个类的方法,整体结构如下:
技术图片

T1_Supervice_T2抽象父类如下:

/**
 * t2中添加十个元素,t1监督t2,t2添加了5个元素时给出通知
 */
public abstract class T1_Supervice_T2 {
    public List<Integer> list = new ArrayList<>();
    public Thread t1;
    public Thread t2;

    public T1_Supervice_T2(){
        t1 = new Thread(()->run1());
        t2 = new Thread(()->run2());
    }

    public void execute(){
        t1.start();
        t2.start();
    }

    public abstract void run1();

    public abstract void run2();

}

测试类如下:

public class T1_Supervice_T2_Test {
    public static void main(String[] args) {
//        T1_Supervice_T2 test = new T1_Supervice_T2_Synchronized();
//        T1_Supervice_T2 test = new T1_Supervice_T2_ReentrantLock();
//        T1_Supervice_T2 test = new T1_Supervice_T2_CountDownLatch();
//        T1_Supervice_T2 test = new T1_Supervice_T2_CycliBarrier();
//        T1_Supervice_T2 test = new T1_Supervice_T2_Semaphore();
        T1_Supervice_T2 test = new T1_Supervice_T2_LockSupport();
        test.execute();
    }
}

方法

第1、2种方法是利用生产者消费者范式来写,范式如下:

等待方(消费者):
    获得对象锁
    条件不满足,则等待
    条件满足,则执行
通知方(生产者):
    获得对象锁
    改变条件
    通知在等待对象上的线程

基于Synchronized

注意:wait和notify必须要用在synchronized中,在外面会报错

public class T1_Supervice_T2_Synchronized extends T1_Supervice_T2 {
    private Object lock = new Object();
    @Override
    public void run1() {
        synchronized (lock){
            while (list.size() != 5){
                try {
                    lock.wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("---------------list has 5 elements!---------------");
                lock.notify();
            }
        }
    }

    @Override
    public void run2() {
        synchronized (lock){
            for (int i = 0; i < 10; i++) {
                System.out.println("list adds " + (i+1) + "th element");
                list.add(i);
                if(list.size() == 5){
                    lock.notify();
                    try {
                        lock.wait();
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

基于ReentrantLock

注意: await方法和signal方法得在lock中间。并要显示的解锁
这里用两个条件,逻辑更加清晰一点

public class T1_Supervice_T2_ReentrantLock extends T1_Supervice_T2 {
    private ReentrantLock lock = new ReentrantLock();
    private Condition turn_t1 = lock.newCondition();
    private Condition turn_t2 = lock.newCondition();
    @Override
    public void run1() {
        try {
            lock.lock();
            while (list.size() != 5)
                turn_t1.await();
            System.out.println("---------------list has 5 elements!---------------");
            turn_t2.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    @Override
    public void run2() {
        try {
            lock.lock();
            for (int i = 0; i < 10; i++) {
                System.out.println("list adds " + (i + 1) + "th element");
                list.add(i);
                if (list.size() == 5) {
                    turn_t1.signal();
                    turn_t2.await();
                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

基于CountDownLatch

CountDownLatch是一个门栓,到达某个一定数量后,就允许线程继续执行下去,但每次只能设置一次,因此在t2 线程中有一个再一次的赋值
调用:await和countDown方法

public class T1_Supervice_T2_CountDownLatch extends T1_Supervice_T2 {
    private CountDownLatch latch = new CountDownLatch(1);
    @Override
    public void run1() {
        if(list.size() != 5){
            try {
                latch.await();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println("---------------list has 5 elements!---------------");
        latch.countDown();
    }

    @Override
    public void run2() {
        for (int i = 0; i < 10; i++) {
            System.out.println("list adds " + (i+1) + "th element");
            list.add(i);
            if(list.size() == 5){
                latch.countDown();
                latch = new CountDownLatch(1);
                try {
                    latch.await();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

基于CycliBarrier

CycliBarrier是循环栅栏,同时到达一定数量后,允许所有线程继续执行,同时恢复到设置的初始值。
只需要调用await方法即可

public class T1_Supervice_T2_CycliBarrier extends T1_Supervice_T2{
    CyclicBarrier barrier = new CyclicBarrier(2);
    @Override
    public void run1() {
        if(list.size() != 5){
            try {
                barrier.await();
                System.out.println("---------------list has 5 elements!---------------");
                barrier.await();
            }catch (Exception e){
                e.printStackTrace();
            }
        }

    }

    @Override
    public void run2() {
        for (int i = 0; i < 10; i++) {
            System.out.println("list adds " + (i+1) + "th element");
            list.add(i);
            if(list.size() == 5){
                try {
                    barrier.await();
                    barrier.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

基于Semaphore

Semaphore是信号量,用来限流的,同时允许n 个线程运行,主要方法acquire用来请求获取使用权,为0时阻塞。release是释放。
这里实际上没有用到Semaphore,主要是join启到了调整运行顺序。

public class T1_Supervice_T2_Semaphore extends T1_Supervice_T2{
    private Semaphore semaphore = new Semaphore(1);

    @Override
    public void execute(){
        t2.start();
    }

    @Override
    public void run1() {
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("---------------list has 5 elements!---------------");
        semaphore.release();
    }

    @Override
    public void run2() {
        try {
            //semaphore.acquire();
            for (int i = 0; i < 10; i++) {
                System.out.println("list adds " + (i + 1) + "th element");
                list.add(i);
                if (list.size() == 5) {
                    semaphore.release();
                    t1.start();
                    t1.join();
                    semaphore.acquire();

                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

基于LockSupport

LockSupport是JUC中一个支持性包。park可以使线程进入waiting状态。unpark则对应线程退出waiting状态。这个不需要在锁下使用,比较灵活,且代码简洁。

public class T1_Supervice_T2_LockSupport extends T1_Supervice_T2 {
    @Override
    public void run1() {
        if(list.size() != 5){
            LockSupport.park();
        }
        System.out.println("---------------list has 5 elements!---------------");
        LockSupport.unpark(t2);
    }

    @Override
    public void run2() {
        for (int i = 0; i < 10; i++) {
            System.out.println("list adds " + (i+1) + "th element");
            list.add(i);
            if(list.size() == 5){
                LockSupport.unpark(t1);
                LockSupport.park();
            }
        }
    }
}

一道多线程面试题的多种解决方法

标签:cut   循环   int   消费   rac   进入   tac   bottom   ++   

原文地址:https://www.cnblogs.com/muche-moqi/p/13168651.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!