关于多线程通讯地一个疑问

[size=large]新手学JAVA,学到多线程,编了一个简单地电脑城进出货模拟系统。
代码有点长,主要部分如下所述:
有三方:厂家,电脑城,顾客
厂家2个,一个生产主板,一个生产显卡。
顾客有2个,他们各自不断购买主板和显卡。
电脑城有一个,卖显卡和主板。[/size]
限于篇幅问题,摘录主要代码如下:
--------------------------[b]厂家类[/b]---------------------------------------
[code="java"]public class Mainboardretailer implements Runnable// 主板厂家
{

public void run()
{
while(true)
{
电脑城.stockMainboard(deliverMB("MSI"));//不断向电脑城供货
}
}

public Mainboard deliverMB(String 主板名)
{
    return 主板;
}

}
public class Videocardretailer implements Runnable// 显卡厂家
{

public void run()
{
while(true)
{
电脑城.stockVideocard(deliverVC("ATI"));//不断向电脑城供货
}
}
public videocard deliverMB(String 显卡名)
{
return 显卡;
}

}[/code]
-------------------------------------[b]顾客类[/b]-------------------------------------------
[code="java"]public class customer implements Runnable
{
public void run()
{
while(true)
{
buyVC("ATI");
//顾客不断购买显卡和主板
buyMB("MSI");
}
}

}[/code]
-----------------------------[b]电脑城类[/b]-----------------------------------------
[code="java"]public class ComputerCenter
{
int MAXVCSTORE = 100;//货仓容量
int MAXMBSTORE = 100;//货仓容量
private static LinkedList VideocardQty = new LinkedList();//显卡货仓
private static LinkedList MainboardQty = new LinkedList();//主板货仓

public synchronized void stockVideocard(Videocard VCname)
{
    if(VideocardQty.size() >= MAXVCSTORE)
    {
        System.out.println("ComputerCenter: the VC storage is MAX");
        try 
        {
            wait();//---------------------当存货过多时。通知厂商等待。
        } catch (InterruptedException e) 
        {
            e.printStackTrace();
        }
    }
        VideocardQty.add(VCname);
        notify();//----------------------------唤醒消费者消费
}

public synchronized void stockMainboard(Mainboard MBname)
{
    if(MainboardQty.size() >= MAXVCSTORE)
    {
        System.out.println("ComputerCenter: the MB storage is MAX");
        try 
        {
            wait();//----------------------当存货过多时。通知厂商等待。
        } catch (InterruptedException e) 
        {
            e.printStackTrace();
        }
    }
        MainboardQty.add(MBname);
        notify();//-----------------------------唤醒消费者消费
}

public synchronized Videocard sellVideocard(String VCname)
{
    if(VideocardQty.size() <= 0)
    {
        try
        {
            wait();//-----------------没有存货时,通知消费者等待
        }catch(Exception e)
        {
            e.printStackTrace();
        }
    }
        notify();//----------------------------------唤醒厂商
        return MyVideocard;
}

public synchronized Mainboard sellMainboard(String MBname)
{
    if(MainboardQty.size() <= 0)
    {
        try
        {
            wait();//-----------------没有存货时,通知消费者等待
        }catch(Exception e)
        {
            e.printStackTrace();
        }
    }
        notify();//----------------------------------唤醒厂商
        return MyMainboard;
}

public static void main(String[] args)
{
    ComputerCenter MyCC = new ComputerCenter();
    new customer(MyCC,"Jack").start();
    new customer(MyCC,"Tom").start();
    new Mainboardretailer(MyCC).start();
    new Videocardretailer(MyCC).start();
}

}[/code]

[size=large]现在出现了这样的一个问题:
1.如果有两个消费者同时等待,厂家生产后唤醒其中消费者A,消费者A购买完毕后会唤醒消费者B--不合逻辑。
2.如果购买显卡地消费者A在等待,电脑城从主板厂商进货了主板以后会唤醒消费者A。同样的情况也发生在购买主板的消费者B身上。
3.如果两家厂商在等待消费者购买商品,此时消费者A购买了主板,货仓主板数量-1,然后有可能唤醒显卡厂商而没有唤醒主板厂商进行生产。
4.如果两家厂商正在等待消费者购买商品,此时显卡厂商被唤醒后,可能立刻唤醒主板厂商生产主板,令到商品数量超出仓库上限。[/size]

我想,有没有什么办法,可以[b]指定唤醒某个线程[/b]?如果可以,那问题就容易解决了。

6个回答

没怎么写过多线程,拿来练练手,未必正确。

[code="java"]
public class Product {
private String name;
private int serial;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getSerial() {
    return serial;
}

public void setSerial(int serial) {
    this.serial = serial;
}

}
[/code]
[code="java"]
import java.util.ArrayDeque;

public class StockManager {
private final Object producerLock = new Object();
private final Object consumerLock = new Object();
private final int MAX_SIZE = 10;

private final ArrayDeque<Product> queue = new ArrayDeque<Product>();

public void stock(Product p) throws InterruptedException {
    synchronized (producerLock) {
        if (queue.size() >= MAX_SIZE) {
            producerLock.wait();
        }
    }
    synchronized (queue) {
        queue.add(p);
    }
    synchronized (consumerLock) {
        consumerLock.notify();
    }
}

public synchronized Product purchase() throws InterruptedException {
    synchronized (consumerLock) {
        if (queue.size() <= 0) {
            consumerLock.wait();
        }
    }
    Product product = null;
    synchronized (queue) {
        product = queue.remove();
    }
    synchronized (producerLock) {
        producerLock.notify();
    }
    return product;
}

}
[/code]
[code="java"]
import java.util.logging.Logger;

public class Producer implements Runnable{
private static final Logger log = Logger.getLogger(Producer.class.getName());

private String productName = null;
private int serial = 0;
private StockManager stockManager;

public void setProductName(String productName) {
    this.productName = productName;
}

public void setStockManager(StockManager stockManager) {
    this.stockManager = stockManager;
}

public Product produce() {
    final Product p = new Product();
    p.setName(productName);
    p.setSerial(++serial);
    return p;
}

@Override
public void run() {
    try {
        for( int i = 0; i < 20; i++) {
            deliver();
        }
        Thread.sleep(30 * 1000);
        while (true) {
            deliver();
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private void deliver() throws InterruptedException {
    final long s = System.currentTimeMillis();
    final Product product = produce();
    stockManager.stock(product);
    final long time = System.currentTimeMillis() - s;
    if (time > 10) {
        log.info(product.getName() + ", No. " +
                product.getSerial() + " took " +
                time + " milli seconds to finish." );
    }
}

}
[/code]
[code="java"]
import java.util.logging.Logger;

public class Consumer implements Runnable {
private static final Logger log = Logger.getLogger(Consumer.class.getName());

private String name;
private StockManager[] stockManagers;

public void setName(String name) {
    this.name = name;
}

public void setStockManagers(StockManager[] stockManagers) {
    this.stockManagers = stockManagers;
}

@Override
public void run() {
    for (int i = 0; i < 50; i++) {
        final double v = Math.random() * stockManagers.length;
        final int k = (int) Math.floor(v);
        try {
            final long s = System.currentTimeMillis();
            final Product product = stockManagers[k].purchase();
            final long time = System.currentTimeMillis() - s;
            String l = "";
            if (time > 10) {
                l += "after " + time + " milli seconds of waiting, ";
            }
            l += (name + " bought product " + product.getName()
                    + ", serial No. " + product.getSerial());
            log.info(l);
        } catch (InterruptedException e) {
            e.printStackTrace();
            break;
        }

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
            break;
        }
    }
}

}
[/code]
[code="java"]
public class Mall {
public static void main(String[] args) {
final StockManager mb = new StockManager();
final Producer pmb = new Producer();
pmb.setProductName("Motherboard");
pmb.setStockManager(mb);

    final StockManager vd = new StockManager();
    final Producer pvd = new Producer();
    pvd.setProductName("Video Card");
    pvd.setStockManager(vd);

    final StockManager[] stockManagers = new StockManager[2];
    stockManagers[0] = mb;
    stockManagers[1] = vd;

    final Consumer c1 = new Consumer();
    c1.setName("C1");
    c1.setStockManagers(stockManagers);

    final Consumer c2 = new Consumer();
    c2.setName("C2");
    c2.setStockManagers(stockManagers);

    new Thread(c1).start();
    new Thread(c2).start();

    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    new Thread(pmb).start();
    new Thread(pvd).start();
}

}
[/code]

1.消费者由生产唤醒,每次唤醒队列中的一个,A买完就走,不要多管闲事
2.不同的产品在不同的队列中,不要排别人的队
3.4.同2

主要问题是 显卡 没了,等显卡送来,主板没了等主板送来。
不能人家来买显卡的,你进来一个主板也通知人家来买。
写了个例子,你看看。
[code="java"]class ComputerCenter {
private static Object VIDEO = new Object();
private static Object MAINB = new Object();
private static Integer videoN = 10;
private static Integer MainbN = 10;

public void stockVd() {
    synchronized(VIDEO) {
        if(videoN >= 20) {
            System.out.println("ComputerCenter: the VC storage is MAX");   
            try {VIDEO.wait();} catch (Exception e) {}   
        }
        System.out.println("ComputerCenter: 厂商送来一块显卡。");
        videoN++;
        VIDEO.notify();//----------------------------唤醒消费者消费
    }
}

public void stockMd() {
    synchronized(MAINB) {
        if(MainbN >= 20) {
            System.out.println("ComputerCenter: the MB storage is MAX");   
            try {MAINB.wait();} catch (Exception e) {}      
        }
        System.out.println("ComputerCenter: 厂商送来一块主板。");
        MainbN++;
        MAINB.notify();//-----------------------------唤醒消费者消费
    }
}

public void sellVd(String name) {   
    synchronized(VIDEO) {
        if(videoN <= 0) {
            System.out.println("没有显卡了,等等,马上送来!!!!!!!!!");   
            try {VIDEO.wait();} catch (Exception e) {}    
        }
        System.out.println(name + " : 买走一块显卡。");
        videoN--;
        VIDEO.notify();//----------------------------------唤醒厂商
    }
}

public void sellMd(String name)  {
    synchronized(MAINB) {
        if(MainbN <= 0) {  
            System.out.println("没有主板了,等等,马上送来!!!!!!!!!!");   
            try {MAINB.wait();} catch (Exception e) {} 
        }
        System.out.println(name + " : 买走一块主板。");
        MainbN--;
        MAINB.notify();//----------------------------------唤醒厂商
    }
}


public static void main(String[] args) {
    ComputerCenter MyCC = new ComputerCenter();   
    new customer(MyCC,"Jack").start();
    new customer(MyCC,"Tom").start();  
    new Md_retailer(MyCC).start();
    new Vd_retailer(MyCC).start();
}

}

class customer extends Thread {
private ComputerCenter cc;
private String name;
public customer(ComputerCenter cc,String name) {
this.cc = cc;
this.name = name;
}

public void run() {
    while(true) {
        //try {Thread.sleep(10);} catch (Exception e) {}      
        //顾客不断购买显卡和主板   
        cc.sellVd(name);    cc.sellMd(name);
    }
}  

}
class Md_retailer extends Thread {
private ComputerCenter cc;
public Md_retailer(ComputerCenter cc) {
this.cc = cc;
}

public void run() {
    while(true) {
        try {Thread.sleep(10);} catch (Exception e) {}      
        cc.stockMd();
    }
}  

}
class Vd_retailer extends Thread {
private ComputerCenter cc;
public Vd_retailer(ComputerCenter cc) {
this.cc = cc;
}

public void run() {
    while(true) {
        try {Thread.sleep(10);} catch (Exception e) {}      
        cc.stockVd();
    }
}  

}[/code]

注释掉不同地方的 try {Thread.sleep(10);} catch (Exception e) {} 可以模拟不同的情况。

噢,对不起,我是没有考虑这个情况。
你说的那种方法应该能够实现,我等待后再判断的方法试了试,要使,你看看
[code="java"]class ComputerCenter {
private static Object VIDEO = new Object();
private static Object MAINB = new Object();
private static Integer videoN = 10;
private static Integer MainbN = 10;

public void stockVd() {
    synchronized(VIDEO) {
        if(videoN >= 20) {
            System.out.println("ComputerCenter: the VC storage is MAX");   
            try {VIDEO.wait();} catch (Exception e) {}   
        }
        videoN++;
        System.out.println("ComputerCenter: 厂商送来一块显卡。" + videoN);
        VIDEO.notify();//----------------------------唤醒消费者消费
    }
}

public void stockMd() {
    synchronized(MAINB) {
        if(MainbN >= 20) {
            System.out.println("ComputerCenter: the MB storage is MAX");   
            try {MAINB.wait();} catch (Exception e) {}      
        }
        MainbN++;
        System.out.println("ComputerCenter: 厂商送来一块主板。"+MainbN);
        MAINB.notify();//-----------------------------唤醒消费者消费
    }
}

public void sellVd(String name) {   
    synchronized(VIDEO) {
        if(videoN <= 0) {
            System.out.println("没有显卡了,等等,马上送来!!!!!!!!!");   
            try {VIDEO.wait();} catch (Exception e) {}    
        }
        if(videoN >0) {
            System.out.println(name + " : 买走一块显卡。"+videoN);
            videoN--;
        }
        VIDEO.notify();//----------------------------------唤醒厂商
    }
}

public void sellMd(String name)  {
    synchronized(MAINB) {
        if(MainbN <= 0) {
            System.out.println("没有主板了,等等,马上送来!!!!!!!!!!");   
            try {MAINB.wait();} catch (Exception e) {} 
        }
        if(MainbN >0) {
            System.out.println(name + " : 买走一块主板。"+MainbN);
            MainbN--;
        }
        MAINB.notify();//----------------------------------唤醒厂商
    }
}

public static void main(String[] args) {
    ComputerCenter MyCC = new ComputerCenter();   
    new customer(MyCC,"Jack").start();
    new customer(MyCC,"Tom").start();  
    new Md_retailer(MyCC).start();
    new Vd_retailer(MyCC).start();
}

}

class customer extends Thread {
private ComputerCenter cc;
private String name;
public customer(ComputerCenter cc,String name) {
this.cc = cc;
this.name = name;
}

public void run() {
    while(true) {
        //try {Thread.sleep(10);} catch (Exception e) {}      
        //顾客不断购买显卡和主板   
        cc.sellVd(name);    cc.sellMd(name);
    }
}  

}
class Md_retailer extends Thread {
private ComputerCenter cc;
public Md_retailer(ComputerCenter cc) {
this.cc = cc;
}

public void run() {
    while(true) {
        try {Thread.sleep(10);} catch (Exception e) {}      
        cc.stockMd();
    }
}  

}
class Vd_retailer extends Thread {
private ComputerCenter cc;
public Vd_retailer(ComputerCenter cc) {
this.cc = cc;
}

public void run() {
    while(true) {
        try {Thread.sleep(10);} catch (Exception e) {}      
        cc.stockVd();
    }
}  

}[/code]

恩,是有点儿,这样你看行不行,也就是顾客在买的时候,如果没有等待,买完后也不通知。电脑城在进货满之后不等待(或者sleep也行,让出cpu,现实中像是卖一段时间在进货),没进一个货就通知一声,只通知一个顾客。
[code="java"]
class ComputerCenter {
private static Object VIDEO = new Object();
private static Object MAINB = new Object();
private static Integer videoN = 10;
private static Integer MainbN = 10;

public void stockVd() {
    synchronized(VIDEO) {
        if(videoN >= 20) {
            System.out.println("ComputerCenter: the VC storage is MAX");   
        } else {
            videoN++;
            System.out.println("ComputerCenter: 厂商送来一块显卡。" + videoN);
            VIDEO.notify();//----------------------------唤醒消费者消费
        }
    }
}

public void stockMd() {
    synchronized(MAINB) {
        if(MainbN >= 20) {
            System.out.println("ComputerCenter: the MB storage is MAX");   
        } else {
            MainbN++;
            System.out.println("ComputerCenter: 厂商送来一块主板。"+MainbN);
            MAINB.notify();//-----------------------------唤醒消费者消费
        }
    }
}

public void sellVd(String name) {   
    synchronized(VIDEO) {
        if(videoN <= 0) {
            System.out.println("没有显卡了,等等,马上送来!!!!!!!!!");   
            try {VIDEO.wait();} catch (Exception e) {}    
        }
        System.out.println(name + " : 买走一块显卡。"+videoN);
        videoN--;
    }
}

public void sellMd(String name)  {
    synchronized(MAINB) {
        if(MainbN <= 0) {
            System.out.println("没有主板了,等等,马上送来!!!!!!!!!!");   
            try {MAINB.wait();} catch (Exception e) {} 
        }
            System.out.println(name + " : 买走一块主板。"+MainbN);
            MainbN--;
    }
}

public static void main(String[] args) {
    ComputerCenter MyCC = new ComputerCenter();   
    new customer(MyCC,"Jack").start();
    new customer(MyCC,"Tom").start();  
    new Md_retailer(MyCC).start();
    new Vd_retailer(MyCC).start();
}

}

class customer extends Thread {
private ComputerCenter cc;
private String name;
public customer(ComputerCenter cc,String name) {
this.cc = cc;
this.name = name;
}

public void run() {
    while(true) {
        //try {Thread.sleep(10);} catch (Exception e) {}      
        //顾客不断购买显卡和主板   
        cc.sellVd(name);    cc.sellMd(name);
    }
}  

}
class Md_retailer extends Thread {
private ComputerCenter cc;
public Md_retailer(ComputerCenter cc) {
this.cc = cc;
}

public void run() {
    while(true) {
        try {Thread.sleep(10);} catch (Exception e) {}      
        cc.stockMd();
    }
}  

}
class Vd_retailer extends Thread {
private ComputerCenter cc;
public Vd_retailer(ComputerCenter cc) {
this.cc = cc;
}

public void run() {
    while(true) {
        try {Thread.sleep(10);} catch (Exception e) {}      
        cc.stockVd();
    }
}  

}[/code]

lz基本的问题就是,厂商和客户等的不是同一把锁,所以要分别对待。然后就简单了。不过我的StockManager看着也不怎么对劲儿。 :oops:

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐