线程生产者 - 消费者java

时间:2016-01-02 19:06:33

标签: java multithreading producer-consumer locks

我必须完成一个练习,我必须使用至少有一个生产者线程和x个消费者线程的生产者/消费者模式在我的文件夹路径中找到“.java”文件。

ProducerConsumer级: 首先,我试图在生产者完成查找文件时停止使用者,将while循环从true设置为false,这不起作用。它不起作用,因为线程仍在运行,显然只是没有做任何有用的事情。现在我使用closePool()函数(也是)。

因此,如果我不忍受名为locka的锁定,该功能确实有效。这基本上是我不明白的事情。

所以,如果我有

    loka.lock();
    ende = false;
    loka.unlock();

    while(ende){
        loka.lock();
        System.out.println(xy.getQueue());
        loka.unlock();
    }

closePool()函数永远不会被调用。这是我不明白的事情。如果我在while循环中收起锁,它确实有效,线程就会停止。

的问题:

1)无论如何,ende参数将被设置为false,因此锁定将最终被释放。

2)其次我只锁定了方法的一部分而不是对象?!据我所知,同一对象中其他方法中的其他代码仍然可以同时工作。或者锁定是否同步,并且当它处于锁定状态时我同步整个对象? 在我的理解中,消费者线程中的while循环被锁定,但生产者线程仍将调用closePool();

额外注意:也许我甚至没有以正确的方式设计我的制作人/消费者模式。

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class FindJavaVisitorp extends SimpleFileVisitor<Path> {

    private BlockingQueue<String> xxx = new ArrayBlockingQueue<String>(10);

    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {

        if (file.toString().endsWith(".java")) {

            try {
                xxx.put(file.toString());

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return FileVisitResult.CONTINUE;
    }

    public String getQueue() throws InterruptedException {
        return xxx.take();

    }
}

public class ProducerConsumer {

    private volatile boolean ende = true;
    private Path path;
    private FindJavaVisitorp xy;
    private Lock loka = new ReentrantLock();
    private ExecutorService pepe;

    public ProducerConsumer(Path path, FindJavaVisitorp xy, ExecutorService xyz) {
        this.path = path;
        this.xy = xy;
        pepe = xyz;
    }

    public void produce() throws IOException, InterruptedException {
        Files.walkFileTree(path, xy);
        loka.lock();
        ende = false;
        loka.unlock();
        closePool();

    }

    public void consume() throws InterruptedException {
        while (ende) {
            loka.lock();
            System.out.println(xy.getQueue());
            loka.unlock();

        }
    }

    public void closePool() {
        pepe.shutdown();
        try {
            if (!pepe.awaitTermination(60, TimeUnit.SECONDS)) {
                pepe.shutdownNow();
                if (!pepe.awaitTermination(60, TimeUnit.SECONDS)) {
                    System.err.println("Pool couldn't be terminated!");
                }

            }

        } catch (InterruptedException e) {
            pepe.shutdownNow();

        }
    }
}

public class Test {
    public static void main(String[] args) {
        Path startingDir = Paths.get("/usr/local/");
        FindJavaVisitorp x = new FindJavaVisitorp();
        ExecutorService exec = Executors.newCachedThreadPool();
        final ProducerConsumer pp = new ProducerConsumer(startingDir, x, exec);

        exec.submit(new Runnable() {

            public void run() {
                try {
                    pp.produce();

                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
        // x.printQueue();

        for (int j = 0; j < 5; j++) {

            exec.submit(new Runnable() {

                public void run() {
                    try {
                        pp.consume();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                }
            });
        }
        exec.shutdown();
    }
}

1 个答案:

答案 0 :(得分:1)

  1. 是的,但只有当线程检查它而不是等待其他东西时,在你的情况下才会发生。在while循环中,线程正在等待BlockingQueue,如果队列为空,那么将无法检查标志变量。此外,由于您已经在使用BlockingQueue,因此您不需要锁定。
    在您的示例中,两个关键部分之间没有任何关系。
  2. 。 以下代码将尝试保护loka=false免受任何并发访问。

    loka.lock();
    ende = false;//critical section
    loka.unlock();
    

    以下代码将免于并发访问,并且与上述关键部分互斥。

    while(ende){
        loka.lock();
        System.out.println(xy.getQueue());//critical section
        loka.unlock();
    }
    

    由于这两个关键部分之间并不常见,因此相互排斥无效。由于ende是挥发性的,因此用锁来保护它并不像primitive types already have atomic access那样做任何事情。

        读取和写入对于引用变量和大多数原始变量(除了long和double之外的所有类型)都是原子的     对于声明为volatile的所有变量(包括长变量和双变量),读取和写入都是原子的。
    1. 只有lock() and unlock()`保护块内的代码才会被锁定并发访问。对象本身可以在这些块之外自由地同时执行任何并发(锁定块)任务。
    2. 最后遵循正确的命名约定,并为变量提供有意义的名称。

      主要答案你的问题为什么你的线程仍然在运行是因为他们等待blockingQueue.takeItem()并且除非队列再次被填满,否则无法从中释放它们但是,由于制片人完成了,所以不可能发生这种情况。

      如何避免此行为

      BlockingQueue上没有允许immediate release of waiting threads的方法 我们可以做的一件事是让生产者放置一个LAST_ITEM并让消费者检查他们得到的物品是否是LAST_ITEM,因此他们可以释放自己。

      以下是工作代码。我对变量和方法名称进行了一些修改,使它们更有意义。

      <强> JavaFileVisitor

      package filevisitor;
      
      import java.nio.file.FileVisitResult;
      import java.nio.file.Path;
      import java.nio.file.SimpleFileVisitor;
      import java.nio.file.attribute.BasicFileAttributes;
      import java.util.concurrent.ArrayBlockingQueue;
      import java.util.concurrent.BlockingQueue;
      
      public class JavaFileVisitor extends SimpleFileVisitor<Path> {
          private BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(10);
      
          public static String NO_MORE_ITEMS = "### NO MORE ITEMS ###";
      
          public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
              if (file.toString().endsWith(".java")) {
                  try {
                      blockingQueue.put(file.toString());
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
              return FileVisitResult.CONTINUE;
          }
      
          public String getQueueItem() throws InterruptedException {
              String item = blockingQueue.take();
              if(NO_MORE_ITEMS.equals(item)) {
                  setNoMoreItems();
              }
              return item;
          }
      
          public void setNoMoreItems() {
              try {
                  blockingQueue.put(NO_MORE_ITEMS);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
      

      <强> ProducerConsumer

      package filevisitor;
      
      import java.io.IOException;
      import java.nio.file.Files;
      import java.nio.file.Path;
      
      public class ProducerConsumer {
      
          private Path path;
          private JavaFileVisitor fileVisitor;
      
          public ProducerConsumer(Path path, JavaFileVisitor visitor) {
              this.path = path;
              this.fileVisitor = visitor;
          }
      
          public void produce() throws IOException, InterruptedException {
              Files.walkFileTree(path, fileVisitor);
              fileVisitor.setNoMoreItems();
          }
      
          public void consume() throws InterruptedException {
              while (true) {
                  String item = fileVisitor.getQueueItem();
                  if(JavaFileVisitor.NO_MORE_ITEMS.equals(item)) {
                      break;
                  }
                  System.out.println(item);
              }
          }
      }
      

      <强> ProducerConsumerMain

      package filevisitor;
      
      import java.io.IOException;
      import java.nio.file.Path;
      import java.nio.file.Paths;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      import java.util.concurrent.TimeUnit;
      
      public class ProducerConsumerMain {
          public static void main(String[] args) {
              Path startingDir = Paths.get("src/filevisitor");
              JavaFileVisitor fileVisitor = new JavaFileVisitor();
              ExecutorService executor = Executors.newCachedThreadPool();
              final ProducerConsumer producerConsumer = new ProducerConsumer(startingDir, fileVisitor);
      
              executor.submit(new Runnable() {
                  public void run() {
                      System.out.println("Producer started");
                      try {
                          producerConsumer.produce();
                      } catch (IOException e) {
                          e.printStackTrace();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System.out.println("Producer finished");
                  }
              });
      
              for (int j = 0; j < 5; j++) {
                  executor.submit(new Runnable() {
                      public void run() {
                          String threadName = Thread.currentThread().getName();
                          System.out.println(threadName + " Consumer Started");
                          try {
                              producerConsumer.consume();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                          System.out.println(threadName + " Consumer finished");
                      }
                  });
              }
      
              executor.shutdown();
              System.out.println("Executor shutdown, waiting for threads to finish");
      
              try {
                  executor.awaitTermination(60, TimeUnit.SECONDS);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("Exiting main");
          }
      }
      

      <强>输出

      Producer started
      pool-1-thread-3 Consumer Started
      pool-1-thread-2 Consumer Started
      Executor shutdown, waiting for threads to finish
      pool-1-thread-5 Consumer Started
      pool-1-thread-6 Consumer Started
      pool-1-thread-4 Consumer Started
      src\filevisitor\JavaFileVisitor.java
      src\filevisitor\ProducerConsumerMain.java
      src\filevisitor\ProducerConsumer.java
      pool-1-thread-6 Consumer finished
      pool-1-thread-4 Consumer finished
      pool-1-thread-3 Consumer finished
      pool-1-thread-5 Consumer finished
      Producer finished
      pool-1-thread-2 Consumer finished
      Exiting main