编写SynStack类

最基础的想法如下:Consumer类和Producer类实现Runnable接口,run方法中调用push与pop

public class SynStack {
    private char[] data = new char[6];
    private int cnt=0;
    public  void push(char ch){
        data[cnt]=ch;
        cnt++;
        System.out.println("生产线程,生产第"+cnt+"个产品,此产品是"+ch);
    }
    public  char pop(){
        char ch=data[cnt-1];
        System.out.println("消费线程,消费第"+cnt+"个产品,此产品是"+ch);
        --cnt;
        return ch;
    }
}

问题:无法控制消费与生产哪个先执行,若消费先执行,data[-1]越界,报错,故必须对满或空加以判断,空则无法消费,满则无法继续生产.

加以改进,完整代码如下

public class SynStack {
    private char[] data = new char[6];
    private int cnt=0;
    public synchronized void push(char ch){
        while(cnt == data.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        this.notify();
        data[cnt]=ch;
        cnt++;
        System.out.println("生产线程,生产第"+cnt+"个产品,此产品是"+ch);
    }
    public synchronized char pop(){
        while(cnt == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        this.notify();
        char ch=data[cnt-1];
        System.out.println("消费线程,消费第"+cnt+"个产品,此产品是"+ch);
        --cnt;
        return ch;
    }
}
public class Producer implements Runnable{
    private SynStack ss =null;
    public Producer(SynStack ss){
        this.ss = ss;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub

        char ch;
        for(int i=0;i<20;++i){
        ch =(char)('a'+i);
        ss.push(ch);
        /*try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }*/
        }
    }

}
public class Consumer implements Runnable{
    private SynStack ss = null;
    public Consumer(SynStack ss){
        this.ss = ss;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<20;++i){
        ss.pop();
        }
    }

}
public class Consumer implements Runnable{
    private SynStack ss = null;
    public Consumer(SynStack ss){
        this.ss = ss;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<20;++i){
        ss.pop();
        }
    }

}

解释:

  1. synchronized修饰方法,表示当前线程霸占正在调用该方法的对象(本例中即SynStack的实例ss)。即生产线程执行push时,会先判断ss是否被消费线程霸占,若没有,则生产线程霸占ss,执行push。从而保证了在执行push操作时,不会切到消费线程去执行
  2. wait与notify:aa.wait()表示将执行aa.wait()的线程阻塞,并释放对aa的锁定。若有T1,T2两个线程,T1由于执行过aa.wait()而阻塞,当T2中执行aa.notify()时,即将T1线程唤醒,转为就绪态

运行结果

生产线程,生产第1个产品,此产品是a
生产线程,生产第2个产品,此产品是b
生产线程,生产第3个产品,此产品是c
生产线程,生产第4个产品,此产品是d
生产线程,生产第5个产品,此产品是e
生产线程,生产第6个产品,此产品是f
消费线程,消费第6个产品,此产品是f
生产线程,生产第6个产品,此产品是g
消费线程,消费第6个产品,此产品是g
生产线程,生产第6个产品,此产品是h
消费线程,消费第6个产品,此产品是h
生产线程,生产第6个产品,此产品是i
消费线程,消费第6个产品,此产品是i
生产线程,生产第6个产品,此产品是j
消费线程,消费第6个产品,此产品是j
生产线程,生产第6个产品,此产品是k
消费线程,消费第6个产品,此产品是k
生产线程,生产第6个产品,此产品是l
消费线程,消费第6个产品,此产品是l
生产线程,生产第6个产品,此产品是m
消费线程,消费第6个产品,此产品是m
生产线程,生产第6个产品,此产品是n
消费线程,消费第6个产品,此产品是n
生产线程,生产第6个产品,此产品是o
消费线程,消费第6个产品,此产品是o
生产线程,生产第6个产品,此产品是p
消费线程,消费第6个产品,此产品是p
生产线程,生产第6个产品,此产品是q
消费线程,消费第6个产品,此产品是q
生产线程,生产第6个产品,此产品是r
消费线程,消费第6个产品,此产品是r
生产线程,生产第6个产品,此产品是s
消费线程,消费第6个产品,此产品是s
生产线程,生产第6个产品,此产品是t
消费线程,消费第6个产品,此产品是t
消费线程,消费第5个产品,此产品是e
消费线程,消费第4个产品,此产品是d
消费线程,消费第3个产品,此产品是c
消费线程,消费第2个产品,此产品是b
消费线程,消费第1个产品,此产品是a

还存在问题吗?

  1. 代码中生产20次,消费20次,程序可以正常结束。若不匹配,程序会停住,无法正常结束,也不会继续成产与消费。当然,可以将run()中pop与push放在死循环里,不停的生产与消费。
  2. pop()与push()中对空和满的判断一定要用while吗,换成if判断不行吗?我的主函数中就一个生产者,一个消费者,运行没有问题。若有多个生产者,多个消费者时,必须用while。我用1个生产者,2个消费者测试,提示-1数组越界