2017-07-31 77 views
3

我一直在研究PC问题以了解Java同步和线程间通信。使用代码在底部,产量为使用java同步了解生产者 - 使用者

Producer produced-0 
Producer produced-1 
Producer produced-2 
Consumer consumed-0 
Consumer consumed-1 
Consumer consumed-2 
Producer produced-3 
Producer produced-4 
Producer produced-5 
Consumer consumed-3 
Consumer consumed-4 

但不宜输出是类似如下

Producer produced-0 
Consumer consumed-0 
Producer produced-1 
Consumer consumed-1 
Producer produced-2 
Consumer consumed-2 
Producer produced-3 

我希望这样的输出,因为我的理解是,消费者被通知当方法终止时,生产方法释放后立即生成的值将被锁定。结果等待的用户块,进入同步状态获取锁定消耗产生的值,同时生成器方法被阻塞。该锁在由生产者线程获取的消耗方法的末尾被释放,该线程由于同步而被阻塞,并且随着每个方法由于获取的锁而被阻塞,周期继续。

请让我知道我误解了什么?由于

package MultiThreading; 

//Java program to implement solution of producer 
//consumer problem. 
import java.util.LinkedList; 

public class PCExample2 
{ 
public static void main(String[] args) 
        throws InterruptedException 
{ 
    // Object of a class that has both produce() 
    // and consume() methods 
    final PC pc = new PC(); 

    // Create producer thread 
    Thread t1 = new Thread(new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      try 
      { 
       while (true) { 
        pc.produce(); 
       }     
      } 
      catch(InterruptedException e) 
      { 
       e.printStackTrace(); 
      } 
     } 
    }); 

    // Create consumer thread 
    Thread t2 = new Thread(new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      try 
      { 
       while (true) { 
        pc.consume(); 
       } 
      } 
      catch(InterruptedException e) 
      { 
       e.printStackTrace(); 
      } 
     } 
    }); 

    // Start both threads 
    t1.start(); 
    t2.start(); 

    // t1 finishes before t2 
    t1.join(); 
    t2.join(); 
} 

// This class has a list, producer (adds items to list 
// and consumber (removes items). 
public static class PC 
{ 
    // Create a list shared by producer and consumer 
    // Size of list is 2. 
    LinkedList<Integer> list = new LinkedList<>(); 
    int capacity = 12; 
    int value = 0; 

    // Function called by producer thread 
    public void produce() throws InterruptedException 
    {   
     synchronized (this) 
     { 
      // producer thread waits while list 
      // is full 
      while (list.size()==capacity) 
       wait(); 

      System.out.println("Producer produced-" 
              + value); 

      // to insert the jobs in the list 
      list.add(value++); 

      // notifies the consumer thread that 
      // now it can start consuming 
      notify(); 

      // makes the working of program easier 
      // to understand 
      Thread.sleep(1000); 
     } 
    } 

    // Function called by consumer thread 
    public void consume() throws InterruptedException 
    { 
     synchronized (this) 
     { 
      // consumer thread waits while list 
      // is empty 
      while (list.size()==0) 
       wait(); 

      //to retrive the ifrst job in the list 
      int val = list.removeFirst(); 


      System.out.println("Consumer consumed-" 
              + val); 

      // Wake up producer thread 
      notify(); 

      // and sleep 
      Thread.sleep(1000); 
     } 
    } 
} 
} 
+0

看看:https://stackoverflow.com/questions/2332537/producer-consumer-threads-using-a-queue/37767243#37767243 –

+0

你可能想看看会发生什么,如果你移动这些线程。睡眠(1000)'调出'同步的'块。 –

回答

0

第一个线程调用当前执行的锁(我们称之为线程A)并不一定会在锁的当前所有者线程放弃之后立即获得锁,如果其他线程有自从线程A尝试获取它之后,还要求锁定。没有有序的“队列”。请参阅herehere。因此,从程序的输出来看,好像在生产者释放锁之后,消费者可能没有足够的时间获得锁,然后生产者线程中的循环被重复,并且生产者线程变成另一个循环调用锁(正如其他答案已经指出,Thread.sleep()不会导致睡眠线程放弃锁),并且如果消费者不幸,生产者将重新获得锁,即使消费者在那里第一次。

但是,似乎还有另一个误解。生产者线程永远不会“等待”PC,直到列表包含12个元素,所以消费者线程只能保证在生产者生成至少12个元素时被授予锁定(顺便说一句,当我运行时该程序 - 消费者从来没有得到机会,直到生产者线程在PC上调用wait(),但然后它消耗整个列表)。这也意味着,如果它恰好是消费者的转弯,列表中包含少于12个元素,生产者线程不会被通知,因为它不是等待通知,但只受阻并已,让我们说“期待”或“期待”PC上的锁定(另请参阅here“等待”和“阻止”之间的区别)。因此,即使您将两个Thread.sleep()调用放在同步块之外,从而使消费者线程(希望不应该依赖此)有足够的时间来获取该锁,因此来自消费者线程的调用notify()将不起作用,因为生产者线程永远不会处于等待状态。

要真正确保两个线程交替修改PC,只有在列表大小大于零的情况下,才需要让生产者线程等待,而不是如果列表包含12个(或多个)元素。

+0

不能使用线程优先级修复线程的执行顺序吗?如果两个线程A(1)和B(5)被阻塞,那么JVM不应该为B(5)提供高优先级的资源,从而B获得对资源的锁定。如果在上述情况下消费者线程的优先级高,会怎么样?我试过了,但我仍然看到相同的顺序。 – Britto

+0

@Britto我不是这个问题的专家,我不知道这个问题的答案,所以你将不得不问问别的地方,或通过stackoverflow搜索,也许有问题解决这个问题。 – Stingy

0

注意两个mothods:通知& &了Thread.sleep

Object.notify():

唤醒在此对象监视器上等待的单个线程。如果任何线程正在等待这个对象,则选择其中一个线程来唤醒。这种选择是任意的,并且由实施决定。线程通过调用其中一个等待方法来等待对象的监视器。
在当前线程放弃对该对象的锁定之前,唤醒的线程将无法继续。被唤醒的线程将以通常的方式与其他可能正在主动竞争的线程竞争以在该对象上同步;例如,被唤醒的线程在作为下一个线程来锁定这个对象时没有可靠的特权或缺点。

Thread.sleep()方法:

使当前执行的线程休眠(暂停执行)为指定的毫秒数加纳秒指定数量,受的精度和准确度系统定时器和调度器。 线程不会丢失任何显示器的所有权

好的。现在你知道notify会唤醒一个也监视这个对象的线程,但是被唤醒的线程将竞争对这个对象进行同步。如果制片人通知消费者并释放锁定,然后制片人和消费者站在同一点进行竞争。而Thread.sleep并没有做你想做的工作,它不会像文档说的那样在睡觉时释放锁。所以这可能发生。

总之,Thread.sleep在同步时不太好。即使你删除了它,第一个输出也会因通知的机制而发生。

@安德鲁S的答案将工作。

0

API:被唤醒的线程将与任何其他线程可能是积极争相此对象上进行同步的常规方式进行竞争;例如,被唤醒的线程在作为下一个线程来锁定这个对象时没有可靠的特权或缺点。

移动sleep()同步块外部给其他线程获取锁的优点。