这段代码大多数情况下运行正常,但是某些情况下会出问题。什么时候会出现什么问题?如何修正?
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(); } }
运行结果
正常情况:
异常情况: