2013-04-12 24 views
0

我想在java中的多线程的一个例子。在Java完全参考第7版中有一个关于多线程同步的例子。这个例子工作正常。但是当我稍微添加一行来创建同一类的另一个线程时,这不起作用。请让我知道为什么会发生这种情况。下面给出了这个例子。下面的代码是生产者和消费者的经典例证。如果只有一个生产者,那么当我有2个生产者时它就会正常工作,那么它就会失败。它只是放到15,然后停下来。多线程同步在Java中不工作

class Q { 

    int n; 
    boolean valueSet = false; 

    synchronized int get() { 
     while (!valueSet) { 
      try { 
       wait(); 
      } catch (InterruptedException e) { 
       System.out.println("InterruptedException caught"); 
      } 
     } 
     System.out.println("Got: " + n); 
     valueSet = false; 
     notify(); 
     return n; 
    } 

    synchronized void put(int n) { 
     while (valueSet) { 
      try { 
       wait(); 
      } catch (InterruptedException e) { 
       System.out.println("InterruptedException caught"); 
      } 
     } 
     this.n = n; 
     valueSet = true; 
     System.out.println("Put: " + n); 
     notify(); 
    } 
} 

class Producer implements Runnable { 

    Q q; 

    Producer(Q q) { 
     this.q = q; 
     new Thread(this, "Producer").start(); 
     //new Thread(this, "Producer2").start(); 
    } 

    public void run() { 
     int i = 0; 
     while (true) { 
      q.put(i++); 
     } 
    } 
} 

class Consumer implements Runnable { 

    Q q; 

    Consumer(Q q) { 
     this.q = q; 
     new Thread(this, "Consumer").start(); 
    } 

    @Override 
    public void run() { 
     while (true) { 
      q.get(); 
     } 
    } 
} 

public class PCFixed { 

    public static void main(String[] args) { 
     Q q = new Q(); 
     Producer P1 = new Producer(q); 
     new Consumer(q); 
     Producer P2 = new Producer(q); 
     System.out.println("Press Control-C to stop."); 
    } 
} 
+0

欢迎来到SO。请编辑您的帖子并修复代码缩进以使其可读。 –

+0

你有生产者和消费者坚持彼此等待。使用'JVisualVM'进行线程检查 – yohlulz

+0

**从不**。 ** **永远。在构造函数中启动一个'Thread';这个例子来自哪里?不仅如此,而且你还从构造函数泄漏'this'。 –

回答

1

Q被写为一次只接受一个值。您需要将put更改为布尔型方法 - 如果valueset为true,则返回true,然后正常继续;如果valueset为false,则返回false,并返回而不执行任何操作。然后调用put的方法将需要保持重试,直到它们得到真实的响应。这样多个消费者可以使用相同的Q对象而不会相互干扰。

如果您使用多个生产者,更好的解决方案是使用ConcurrentLinkedQueue,这是一个线程安全队列。生产者将offer整数放入队列,而消费者将整数队列放入poll。多个生产者可以同时使用整数,而不会相互干扰,并且多个消费者可以同时使用整数,而不会相互干扰。

+1

“LinkedBlockingQueue”是比“ConcurrentLinkedQueue”更好的选择,因为消费者可以阻止它。 –

0

您提供的并发示例使用单个boolean标志来检查是否存在信号。

所以这是一个比生产者消费者安排更多的Semaphore安排。处理任意数量的Thread s太简单了。

如果你真的想使用生产者消费者,你将需要一个拥有多个项目的队列。

static final AtomicBoolean run = new AtomicBoolean(true); 

static class Producer implements Runnable { 

    final BlockingQueue<String> blockingQueue; 

    public Producer(BlockingQueue<String> blockingQueue) { 
     this.blockingQueue = blockingQueue; 
    } 

    @Override 
    public void run() { 
     while (run.get()) { 
      blockingQueue.add("Value from " + Thread.currentThread().getName()); 
      try { 
       Thread.sleep(100); 
      } catch (InterruptedException ex) { 
       //doesn't matter. 
      } 
     } 
    } 
} 

static class Consumer implements Runnable { 

    final BlockingQueue<String> blockingQueue; 

    public Consumer(BlockingQueue<String> blockingQueue) { 
     this.blockingQueue = blockingQueue; 
    } 

    @Override 
    public void run() { 
     while (run.get()) { 
      final String item; 
      try { 
       item = blockingQueue.take(); 
      } catch (InterruptedException ex) { 
       return; 
      } 
      System.out.println(item); 
     } 
    } 
} 

public static void main(String[] args) throws InterruptedException { 
    final LinkedBlockingQueue<String> lbq = new LinkedBlockingQueue<>(); 
    final ExecutorService executorService = Executors.newCachedThreadPool(); 
    executorService.submit(new Consumer(lbq)); 
    for (int i = 0; i < 10; ++i) { 
     executorService.submit(new Producer(lbq)); 
    } 
    Thread.sleep(10000); 
    run.set(false); 
    executorService.shutdownNow(); 
} 

这个简单的例子使用LinkedBlockingQueue张贴事件和读取事件。

ProducerStrings放入队列中,并使用它自己的Thread名称(它们每隔100ms执行一次)。 Consumer从队列中取出并打印String

该队列是一个BlockingQueue所以take方法将阻塞如果队列为空。

通过更改将项目添加到ExecutorService的循环,您可以轻松地更改Producer s和Consumer s的数量。试验,看看它是如何工作的。

AtomicBoolean标志允许程序关闭所产生的所有子进程。

0

将每次出现的notify替换为notifyAll