我无法弄清楚这个线程有什么问题,我试图理解多线程

时间:2013-01-12 00:12:40

标签: java multithreading

我很抱歉这太长了,而且可能看起来太多了,但如果你能一目了然地弄清楚什么是错的,请告诉我。

在这个程序中,我尝试在每次获取一个令牌时从键盘输入一些单词(短语)并将其分配给对象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;
}

}

1 个答案:

答案 0 :(得分:8)

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

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

    }

因为你没有递减i。你试图用来摆脱循环的中断机制(通过例外!)也被打破了。

Thread.sleep(15*rd.nextInt(2000))行看起来像黑客,以便在任务中断时获得InterruptedException,但是:

  • 随机毫秒数的睡眠点让我感到惊讶,
  • 只需拨打Thread.interrupted()即可。

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

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


如果我试图实现这一点(使用中断),我会对其进行更改,以便getset不会“吞噬”中断。如果他们看到中断,他们应该:

  • 允许InterruptedException传播(在相关清理后)或
  • 在异常处理程序中再次设置线程的中断标志。