我必须完成一个练习,我必须使用至少有一个生产者线程和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();
}
}
答案 0 :(得分:1)
BlockingQueue
,如果队列为空,那么将无法检查标志变量。此外,由于您已经在使用BlockingQueue,因此您不需要锁定。。
以下代码将尝试保护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的所有变量(包括长变量和双变量),读取和写入都是原子的。
lock() and
unlock()`保护块内的代码才会被锁定并发访问。对象本身可以在这些块之外自由地同时执行任何并发(锁定块)任务。最后遵循正确的命名约定,并为变量提供有意义的名称。
主要答案你的问题为什么你的线程仍然在运行是因为他们等待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