• 阿里多线程笔试题


    这段代码大多数情况下运行正常,但是某些情况下会出问题。什么时候会出现什么问题?如何修正?

    public class MyStack {  
        private List<String> list = new ArrayList<String>();  
      
        public synchronized void push(String value) {  
            synchronized (this) {  
                list.add(value);  
                notify();  
            }  
        }  
      
        public synchronized String pop() throws InterruptedException {  
            synchronized (this) {  
                if (list.size() <= 0) {  
                    wait();  
                }  
                return list.remove(list.size() - 1);  
            }  
        }  
    }  

     分析:

    1,MyStack的push与pop方法都是原子的。由于两个方法中都有synchronized (this),对当前对象加了锁,那么多个线程在执行某个MyStack对象的push、pop操作就是串行的。

    2,假设有三个线程在访问这段代码,其中线程1进行push操作,线程2、线程3执行pop操作。

    3,正常情况是:线程1先启动,往list中添加一个值后,线程2执行pop操作,此时list中已有一个值,那么它就不会wait,接着执行list.remove,完成方法后释放锁。

         接着线程3得以获得pop方法上的锁以进入方法中,发现list.size()已等于0,则进行等待。

    4,异常情况是:线程2先启动,进入pop方法发现list.size等于0,则释放锁等待。线程1启动往list中放入一个值,此时线程3也启动,阻塞在pop方法的synchronized (this)处,

        在线程1执行notify后,会随机唤醒一个在当前对象监视器中等待的线程。

       假设此时被唤醒的是线程3,它将进入synchronized (this)同步块,由于之前线程1已push了一个值,那么if (list.size() <= 0)为true,线程3将remove(0),完成删除。

       线程3执行完pop方法后,将释放锁。等待在wait处的线程2将获得锁继续执行,由于此时的list已无值,list.size()将返回0,那么线程2将执行list.remove(-1),程序抛出出现数组越界错误。

    PS:push方法中的notify将会随机唤醒pop方法中的synchronized(this)和wait这两处的阻塞线程中的一个。

    如何修正:

    在remove前再进行一次判断,判断当前list.size是否大于0。

    if(list.size >0){
      list.remove(list.size() - 1);
    }

    测试:

    MyStack.java

    public class MyStack {
        private List<String> list = new ArrayList<String>();
    
        public synchronized void push(String value) {
            synchronized (this) {
                list.add(value);
                notify();
                System.out.println("push complete");
            }
        }
    
        public synchronized String pop() throws InterruptedException {
            System.out.println(Thread.currentThread().getName() +" entered pop method");
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() +" entered synchronized (this)");
                if (list.size() <= 0) {
                    System.out.println(Thread.currentThread().getName() +" waiting");
                    wait();
                }
                System.out.println(Thread.currentThread().getName() +" start remove List");
                return list.remove(list.size() - 1);
            }
        }
    }

    PushThread.java

    public class PushThread implements Runnable {
        MyStack stack = null;
        PushThread(MyStack stack) {
            this.stack = stack;
        }
        public void run() {
            stack.push("a");
            System.out.println("push thread exit");
        }
    }

    PopThread.java

    public class PopThread implements Runnable{
        MyStack stack = null;
        public PopThread(MyStack stack){
            this.stack = stack;
        }
        
        public void run() {
            try {
                stack.pop();
                System.out.println(Thread.currentThread().getName()+"  pop thread exit");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    Test.java

    public class Test {
        public static void main(String[] args) {
            MyStack stack = new MyStack();
            PushThread push = new PushThread(stack);
            PopThread pop1 = new PopThread(stack);
            PopThread pop2 = new PopThread(stack);
            new Thread(push).start();
            new Thread(pop1,"pop1").start();
            new Thread(pop2,"pop2").start();
        }
    }

    运行结果

    正常情况:

    异常情况:

  • 相关阅读:
    springcloud之配置中心和消息总线(配置中心终结版)
    yaml
    RESTful API
    单元测试Junit5
    IDEA社区版创建web项目
    Mybatis常见面试题
    mybatis逆向工程
    mybatis注解
    延迟加载
    缓存
  • 原文地址:https://www.cnblogs.com/pingh/p/3488414.html
Copyright © 2020-2023  润新知