3

很抱歉,这太长了,而且似乎要求太多,但是如果您一眼就能找出问题所在,请告诉我。

在这个程序中,我尝试在每次获取一个标记时从键盘输入一些单词(短语)并将其分配给一个对象sharedStorer(然后打印分配的值以跟踪输入的内容,因为我有一系列单词要单独输入) . 这是由一个线程完成的(Retriever哪个类的线程implements Runnable

还有另一个线程class TokenReader读取sharedStorer并打印出来。TokenReader等待Retriever输入,当Retriever尝试输入而TokenReader尚未读取前一个令牌时Retriever等待。

我的问题是,最后TokenReader永远等待Retriever它完成了它的任务,所以程序永远不会终止。

这是我用来执行所需任务的所有 4 个类(和 1 个接口)。

package Multithreads;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExerciseTest {


public static void main(String[] args) {

    ExecutorService app=Executors.newFixedThreadPool(2);

    Storer st=new SyncStorer();

    System.out.println("Operation performed\t\t Value");
    try{
    app.execute(new Retriever(st));
    app.execute(new TokenReader(st));
    }catch(Exception e){
        e.printStackTrace();
    }
    app.shutdown();
}

}

package Multithreads;

public interface Storer {

public void set(String token);
public String get();
}

package Multithreads;

import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Scanner;
import java.util.StringTokenizer;

public class Retriever implements Runnable {

private Scanner scanner;
private String token;
private String currentToken;
private Storer sharedStorer;
private Random rd=new Random();
public int tokenLength=0;

public Retriever(Storer st) {

    sharedStorer=st;

}
public Retriever() {

}
@Override
public void run() {

    System.out.println("Enter a phrase");
    scanner = new Scanner(System.in);
    token=scanner.nextLine();
    StringTokenizer tokenizer=new StringTokenizer(token);

    while(tokenizer.hasMoreTokens())
    {
        tokenLength++;
        currentToken=tokenizer.nextToken();
        try{

        Thread.sleep(10*rd.nextInt(2000));
        sharedStorer.set(currentToken);

        }catch(NoSuchElementException e){
            e.printStackTrace();

        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
    System.out.println("Done Inputting The phrase");

}

}

package Multithreads;

import java.util.Random;

public class TokenReader implements Runnable {

private Random rd=new Random();

private Storer sharedStorer;
Retriever rtr=new Retriever();
private int count=rtr.tokenLength;

public TokenReader(Storer st) {
    sharedStorer=st;
}

@Override
public void run() {
String str="null";
    int i=0;
    try {
        while(i <= count){
        Thread.sleep(15*rd.nextInt(2000));
        str=sharedStorer.get();

        }
    } catch (InterruptedException e) {

        e.printStackTrace();
    }

    System.out.println("Consumer done reading");
}


}

package Multithreads;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SyncStorer implements Storer {

private Lock accessLock=new ReentrantLock();

private Condition canInput = accessLock.newCondition();
private Condition canRead = accessLock.newCondition();

private String string="null";
private boolean isEmpty=false;

@Override
public void set(String token) {
    accessLock.lock();
    try
    {
        while(isEmpty){
            System.out.println("Retriever waiting");
            canInput.await();
        }
        string=token;
        isEmpty=true;
        System.out.println("Retriever inputs\t\t "+string);

        canRead.signal();
    }catch(InterruptedException e){
        e.printStackTrace();
    }finally{
        accessLock.unlock();
    }

}

@Override
public String get() {

    accessLock.lock();
    try{
        while(!isEmpty)
        {               
            System.out.println("No token to read");
            canRead.await();
        }
        isEmpty=false;
        System.out.println("TokenReader reads\t\t "+string);


        canInput.signal();
    }catch(InterruptedException e)
    {
        e.printStackTrace();
    }finally{
        accessLock.unlock();
    }
    return string;
}

}
4

1 回答 1

8

导致应用程序永远运行的问题是这是一个无限循环:

    while(i <= count){
        Thread.sleep(15*rd.nextInt(2000));
        str=sharedStorer.get();

    }

因为你没有递减i。您试图用来跳出循环的基于中断的机制(通过异常!)也被破坏了。

Thread.sleep(15*rd.nextInt(2000))行看起来像一个hack,因此您可以InterruptedException在任务被中断时得到一个,但是:

  • 随机数毫秒的睡眠点让我逃脱了,并且
  • 打电话会更简单Thread.interrupted()

此外,该方法无论如何都不可靠,因为中断有可能您等待/测试它之后发生;即在get()通话中。如果那个get()调用永远不会返回,因为它store是空的并且检索器已经结束......那么你将“永远”等待。

最后还有一个问题。app.shutdownNow()如果您希望执行程序服务中断工作线程,则需要调用...


如果我试图实现这个(使用中断),我会改变它,这样getset不会“吞噬”中断。如果他们看到中断,他们应该:

  • 允许InterruptedException传播(在相关清理之后),或
  • 在异常处理程序中再次设置线程的中断标志。
于 2013-01-12T00:32:31.127 回答