java 多线程实现生产者模型问题

流氓T-T兔 2014-01-09 10:46:46

public class Baskert {

private List<String> list = new ArrayList<String>();

private Lock lock = new ReentrantLock();

private Condition setter = lock.newCondition(); //篮子为满的条件

private Condition getter = lock.newCondition(); //篮子为空的条件

public void addApple() throws InterruptedException{
try{
lock.lock();
while(list.size()>=5){
System.out.println(Thread.currentThread().getName()+"--挂起");
setter.await();
}
list.add("红富士");
System.out.println(Thread.currentThread().getName()+"放了一个红富士==="+list.size());
System.out.println("释放2,3");
getter.signalAll();
}finally{
lock.unlock();
}
}

public void removeApple() throws InterruptedException{
try{
lock.lock();
while(list.size()<=0){
System.out.println(Thread.currentThread().getName()+"--挂起");
getter.await();
}
list.remove(0);
System.out.println(Thread.currentThread().getName()+"拿了一个苹果==="+list.size());
System.out.println("释放0,1");
setter.signalAll();
}finally{
lock.unlock();
}
}

public static void main(String[] args){
final Baskert baskert = new Baskert();
new Thread(new Runnable() {

public void run() {
try {
while(true){
baskert.addApple();
}
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}).start();
new Thread(new Runnable() {

public void run() {
try {
while(true){
baskert.addApple();
}
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}).start();
new Thread(new Runnable() {

public void run() {
try {
while(true){
baskert.removeApple();
}
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}).start();
new Thread(new Runnable() {

public void run() {
try {
while(true){
baskert.removeApple();
}
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}).start();
}

}


结果是:
Thread-1放了一个红富士===1
释放2,3
Thread-1放了一个红富士===2
释放2,3
Thread-1放了一个红富士===3
释放2,3
Thread-1放了一个红富士===4
释放2,3
Thread-1放了一个红富士===5
释放2,3
Thread-1--挂起
Thread-0--挂起
Thread-3拿了一个苹果===4
释放0,1
Thread-3拿了一个苹果===3
释放0,1
Thread-3拿了一个苹果===2
释放0,1
Thread-3拿了一个苹果===1
释放0,1
Thread-3拿了一个苹果===0
释放0,1
Thread-3--挂起
Thread-2--挂起
Thread-1放了一个红富士===1
释放2,3
Thread-1放了一个红富士===2

可见只有一个线程把篮子放满或者拿空,挂起之后另一个线程才会操作。
而我觉得结果应该是 只要篮子不满或者不空,放苹果线程和取苹果线程应该是同时进行的,请高手指点为什么会这样
...全文
163 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
流氓T-T兔 2014-01-09
  • 打赏
  • 举报
回复
引用 2 楼 zxcvbnm11920 的回复:
你代码没问题,把苹果数量调大可以出现你要的结果。
数量 50 100,500 结果都一样
流氓T-T兔 2014-01-09
  • 打赏
  • 举报
回复
引用 1 楼 remember1989 的回复:
你的代码没有问题,我重新组织了一下,以便更好地观察结果。
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 多线程生产者-消费者示例
 */
public class Basket {

    public static final int BASKET_CAPACITY = 5;

    private List<String> list = new ArrayList<>();

    private Lock lock = new ReentrantLock();

    private Condition setter = lock.newCondition(); //篮子为满的条件

    private Condition getter = lock.newCondition(); //篮子为空的条件

    // 放入苹果
    public void addApple() throws InterruptedException {
        try {
            lock.lock();
            while (list.size() >= BASKET_CAPACITY) {
                outputThreadMessage("篮子已满,等待取出", false);
                setter.await();
            }
            list.add("红富士");
            outputThreadMessage("放入苹果: 篮子里还有" + list.size(), false);

            getter.signalAll();
        } finally {
            lock.unlock();
        }
    }

    // 取出苹果
    public void removeApple() throws InterruptedException {
        try {
            lock.lock();
            while (list.size() <= 0) {
                outputThreadMessage("篮子已空,等待放入", true);
                getter.await();
            }
            list.remove(0);
            outputThreadMessage("取出苹果: 篮子里还有" + list.size(), true);

            setter.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        final Basket baskert = new Basket();

        // 通过调整 AddAppleTask 和 RemoveAppleTask 的线程数,可以
        // 观察篮子满或空的情况
        
        new Thread(new AddAppleTask(baskert)).start();
        new Thread(new AddAppleTask(baskert)).start();

        new Thread(new RemoveAppleTask(baskert)).start();
        new Thread(new RemoveAppleTask(baskert)).start();
    }

    /**
     * 放入苹果线程
     */
    private static class AddAppleTask implements Runnable {

        private final Basket basket;

        public AddAppleTask(Basket basket) {
            this.basket = basket;
        }

        public void run() {
            try {
                while (waitRandomTime()) {
                    basket.addApple();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 取出苹果线程
     */
    private static class RemoveAppleTask implements Runnable {

        private final Basket basket;

        public RemoveAppleTask(Basket basket) {
            this.basket = basket;
        }

        public void run() {
            try {
                while (waitRandomTime()) {
                    basket.removeApple();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }

    /////////////////////////////////////////////////////////

    public static final Random RANDOM = new Random();

    private static void outputThreadMessage(String message, boolean err) {
        if (err) {
            System.err.printf("[%10s] %s\n", Thread.currentThread().getName(), message);
        } else {
            System.out.printf("[%10s] %s\n", Thread.currentThread().getName(), message);
        }
    }

    private static boolean waitRandomTime() {
        try {
            Thread.sleep(RANDOM.nextInt(1000) + 500);
            return true;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        }
    }
}
谢谢 不过你的输出结果和预期相同,是不是因为你waitRandomTime方法调用了sleep。为什么不用sleep执行顺序就不对,巧合吗
remember1989 2014-01-09
  • 打赏
  • 举报
回复
之前那个有问题,因为控制台的输出并不是严格按照先后顺序来的,所以不能依赖控制台输出来观察多线程的运行,必须先按照先后顺序把消息记下来,然后再一把输出。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 多线程生产者-消费者示例
 */
public class Basket {

    public static final int BASKET_CAPACITY = 5;

    private List<String> list = new ArrayList<>();

    private Lock lock = new ReentrantLock();

    private Condition setter = lock.newCondition(); //篮子为满的条件

    private Condition getter = lock.newCondition(); //篮子为空的条件

    // 放入苹果
    public void addApple() throws InterruptedException {
        try {
            lock.lock();
            while (list.size() >= BASKET_CAPACITY) {
                addMessage("+++ 篮子已满,等待取出");
                setter.await();
            }
            list.add("红富士");
            addMessage("+++ 放入苹果: 篮子里还有" + list.size());

            getter.signalAll();
        } finally {
            lock.unlock();
        }
    }

    // 取出苹果
    public void removeApple() throws InterruptedException {
        try {
            lock.lock();
            while (list.size() <= 0) {
                addMessage("--- 篮子已空,等待放入");
                getter.await();
            }
            list.remove(0);
            addMessage("--- 取出苹果: 篮子里还有" + list.size());

            setter.signalAll();
        } finally {
            lock.unlock();
        }
    }

    /////////////////////////////////////////////////////////

    public static void main(String[] args) throws Exception {
        final Basket baskert = new Basket();

        // 通过调整 AddAppleTask 和 RemoveAppleTask 的线程数,可以
        // 观察篮子满或空的情况

        startTask(new AddAppleTask(baskert));
        startTask(new AddAppleTask(baskert));
        startTask(new AddAppleTask(baskert));

        startTask(new RemoveAppleTask(baskert));
        startTask(new RemoveAppleTask(baskert));

        System.out.println("线程运行中,请等待 10 秒...");

        Thread.sleep(10000);    // 让线程运行 10 秒钟
        stop = true;
        Thread.sleep(2000);     // 等待所有线程停止

        outputMessagesWithOrder();       // 按照次序打印消息
    }

    private static void startTask(Runnable runnable) {
        Thread thread = new Thread(runnable);
        thread.setDaemon(true);
        thread.start();
    }

    /////////////////////////////////////////////////////////

    /**
     * 放入苹果线程
     */
    private static class AddAppleTask implements Runnable {

        private final Basket basket;

        public AddAppleTask(Basket basket) {
            this.basket = basket;
        }

        public void run() {
            try {
                while (waitRandomTime()) {
                    basket.addApple();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 取出苹果线程
     */
    private static class RemoveAppleTask implements Runnable {

        private final Basket basket;

        public RemoveAppleTask(Basket basket) {
            this.basket = basket;
        }

        public void run() {
            try {
                while (waitRandomTime()) {
                    basket.removeApple();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }

    /////////////////////////////////////////////////////////

    public static final Random RANDOM = new Random();

    private static boolean stop = false;

    private static List<String> messages = new ArrayList<>();

    private static final AtomicInteger serial = new AtomicInteger();

    private static synchronized void addMessage(String message) {
        messages.add(String.format("%4d[%8s] %s",
                serial.incrementAndGet(), Thread.currentThread().getName(), message));
    }

    private static void outputMessagesWithOrder() {
        Collections.sort(messages);

        for (String message : messages) {
            System.out.println(message);
        }
    }

    private static boolean waitRandomTime() {
        try {
            Thread.sleep(RANDOM.nextInt(1000) + 500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return !stop;
    }
}
骑士的崛起 2014-01-09
  • 打赏
  • 举报
回复
你代码没问题,把苹果数量调大可以出现你要的结果。
remember1989 2014-01-09
  • 打赏
  • 举报
回复
你的代码没有问题,我重新组织了一下,以便更好地观察结果。
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 多线程生产者-消费者示例
 */
public class Basket {

    public static final int BASKET_CAPACITY = 5;

    private List<String> list = new ArrayList<>();

    private Lock lock = new ReentrantLock();

    private Condition setter = lock.newCondition(); //篮子为满的条件

    private Condition getter = lock.newCondition(); //篮子为空的条件

    // 放入苹果
    public void addApple() throws InterruptedException {
        try {
            lock.lock();
            while (list.size() >= BASKET_CAPACITY) {
                outputThreadMessage("篮子已满,等待取出", false);
                setter.await();
            }
            list.add("红富士");
            outputThreadMessage("放入苹果: 篮子里还有" + list.size(), false);

            getter.signalAll();
        } finally {
            lock.unlock();
        }
    }

    // 取出苹果
    public void removeApple() throws InterruptedException {
        try {
            lock.lock();
            while (list.size() <= 0) {
                outputThreadMessage("篮子已空,等待放入", true);
                getter.await();
            }
            list.remove(0);
            outputThreadMessage("取出苹果: 篮子里还有" + list.size(), true);

            setter.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        final Basket baskert = new Basket();

        // 通过调整 AddAppleTask 和 RemoveAppleTask 的线程数,可以
        // 观察篮子满或空的情况
        
        new Thread(new AddAppleTask(baskert)).start();
        new Thread(new AddAppleTask(baskert)).start();

        new Thread(new RemoveAppleTask(baskert)).start();
        new Thread(new RemoveAppleTask(baskert)).start();
    }

    /**
     * 放入苹果线程
     */
    private static class AddAppleTask implements Runnable {

        private final Basket basket;

        public AddAppleTask(Basket basket) {
            this.basket = basket;
        }

        public void run() {
            try {
                while (waitRandomTime()) {
                    basket.addApple();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 取出苹果线程
     */
    private static class RemoveAppleTask implements Runnable {

        private final Basket basket;

        public RemoveAppleTask(Basket basket) {
            this.basket = basket;
        }

        public void run() {
            try {
                while (waitRandomTime()) {
                    basket.removeApple();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }

    /////////////////////////////////////////////////////////

    public static final Random RANDOM = new Random();

    private static void outputThreadMessage(String message, boolean err) {
        if (err) {
            System.err.printf("[%10s] %s\n", Thread.currentThread().getName(), message);
        } else {
            System.out.printf("[%10s] %s\n", Thread.currentThread().getName(), message);
        }
    }

    private static boolean waitRandomTime() {
        try {
            Thread.sleep(RANDOM.nextInt(1000) + 500);
            return true;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        }
    }
}
骑士的崛起 2014-01-09
  • 打赏
  • 举报
回复
引用 5 楼 wq568907978 的回复:
[quote=引用 2 楼 zxcvbnm11920 的回复:] 你代码没问题,把苹果数量调大可以出现你要的结果。
数量 50 100,500 结果都一样[/quote] 你代码耗时太低了,应该是计算线程优先级的问题,我这里调到30就能出现你想要的结果,加上Thread.sleep(1000)调到20就能出现你要的结果。

62,621

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧