pingh14

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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

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();
    }
}

 

运行结果

正常情况:

异常情况:

posted on 2013-12-24 01:24  pingh14  阅读(1081)  评论(0编辑  收藏  举报