-3

我很头疼用java杀死一个线程......我在stackoverflow上看到了很多主题,但我没有让他们处理我的代码......有人可以解释一下我如何能够在不使用deprecated的情况下杀死一个线程函数(如停止),请以安全的方式(我的线程也在运行一个套接字:DatagramSocket)。

类 p2p_app->

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
//import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class p2p_app {

private String ip;
private Integer porta;
private LinkedList<Vizinho> vizinhos;
private String pathSharedFolder;
private String pathBootStrap;
private int exit;
//private Thread send;
//private Thread receive;
private UDPreceive udpR;

public p2p_app(String args[]) throws IOException {
    this.ip =  InetAddress.getLocalHost().getHostAddress();         
    this.vizinhos = new LinkedList<Vizinho>();
    this.exit = 0;
    //this.send=null;
    //this.receive=null;
    this.udpR=null;
    if(args.length==2){
        this.pathSharedFolder=args[0];
        this.pathBootStrap=args[1];
        System.out.println(pathSharedFolder);
        System.out.println(pathBootStrap);
    }
    else{
        this.pathSharedFolder="./";
        this.pathBootStrap="./p2p_bootstrap.conf";
        System.out.println(pathSharedFolder);
        System.out.println(pathBootStrap);
    }

    readFile(this.pathBootStrap);
    createSharedFolder(this.pathSharedFolder);
}

public void assign(String tipo,String info) //tratar o file bootstrap.conf
{

     Tipos currentTipos = Tipos.valueOf(tipo.toUpperCase());

        switch(currentTipos){

        case PATH:  if(this.pathSharedFolder==null)
                        this.pathSharedFolder = info; 
                    break;

        case PORTA: this.porta = Integer.parseInt(info);
                    break;

        case IP:    StringTokenizer st = new StringTokenizer(info,":");
                    st.nextElement();
                    String[] tokens = info.split(":");      
                    Vizinho s = new     Vizinho(tokens[0],Integer.parseInt(tokens[1]));
                    this.vizinhos.add(s);
                    break;
        default:
            break;
    }


}

public void trataLine(String line){

    Pattern p = Pattern.compile("[\\w\\./:]+");
    Matcher m = p.matcher(line);
    String tipo = "";

    while(m.find()){

        if(tipo.compareTo("")==0)
            tipo = m.group();

        else assign(tipo,m.group());

    }

}

public void readFile(String path) throws IOException{ //modifiquei este codigo para     ver se existe ou nao o ficheiro bootstrap (VASCO)

    String line;
    Pattern p = Pattern.compile("\\$");

    File f = new File(path);

    if(f.exists()){
        BufferedReader br;
        br = new BufferedReader(new FileReader(path));



        while ((line = br.readLine()) != null) {

            Matcher m = p.matcher(line);

            if(m.find() == true)
                trataLine(line);

        }

        br.close();
    }
    else{
        System.out.println("FILE :: BOOTSTRAP.CONF : Doesn't exist.");
    }
}



public void createSharedFolder(String path) {

    if(!(new File(path).exists()))  
        new File(path).mkdir();

}

public enum Tipos {

    PATH,
    PORTA,
    T1,
    T2,
    T3,
    R,
    M,
    K,
    IP
}

public String getIp(){

    return this.ip;
}

public Integer getPorta(){

    return this.porta;
}

public int getExit(){
    return this.exit;
}

public void setExit(int exit){
    this.exit = exit;
}



public LinkedList<Vizinho> getVizinhos(){

    LinkedList<Vizinho> aux = new LinkedList<Vizinho>();
    for(Vizinho c : this.vizinhos) aux.add(c);
    return aux;     
}

public String toString(){

    StringBuilder s = new StringBuilder();
    s.append("IP:"+this.ip + "\n");
    s.append("Porta:"+ this.porta +"\n");
    s.append("Directory:" + this.pathSharedFolder + "\n");
    s.append("-----Vizinhos-----");
    for(Vizinho c : this.vizinhos)
    s.append(c.toString());

    return s.toString();    
}

public void initThreads(p2p_app p2p){
    //UDPreceive udpR = new UDPreceive(p2p);
    this.udpR = new UDPreceive(p2p);
    //UDPsend udpS = new UDPsend(p2p);

    //this.receive = new Thread(udpR);
    Thread t = new Thread(udpR);
    //this.send = new Thread(udpS);

    t.start();
    //this.receive.start();
    //this.send.start();
}

@SuppressWarnings("deprecation")
public void stopThreads(){
    this.udpR.stopRun();
    //this.receive.interrupt();
    //this.receive.stop();
    //this.receive.toString();
    //this.send.interrupt();
    //this.send.toString();
}

public void menu(){
    System.out.println("1:Hello");
    System.out.println("2:Vasco");
    System.out.println("3:Exit");
}

public int choiceMenu(int i){
    int numRowsInConsole = 60;
    final String ESC = "\033[";

    switch(i){
    case 1:
        System.out.println("FUNCIONOU HELLO");
        System.out.print(ESC + "2J");
        /*for (int ii=0; ii<numRowsInConsole; ii++) {
            // scroll down one line
            System.out.println("");
        }*/
        break;
    case 2:
        System.out.println("FUNCIONOU VASCO");
        System.out.print(ESC + "2J");
        break;
    case 3:
        i=-1;
        System.out.print(ESC + "2J");
        break;
    default:
    }
    return i;

}

public static void main(String[] args) throws IOException {
    int i;

    p2p_app p2p = new p2p_app(args);
    //p2p.initThreads(p2p);
    System.out.println(p2p.toString());

    Scanner sc = new Scanner(System.in);
    while(p2p.getExit() != -1){
        p2p.menu();
        i = sc.nextInt();
        p2p.setExit(p2p.choiceMenu(i));
        System.out.println(p2p.getExit());
    }

    System.out.println("Woot woot!");

    //p2p.stopThreads();


}

}

类 UDPreceive->

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;


public class UDPreceive implements Runnable {
private p2p_app p2p;
private DatagramPacket p;

public volatile boolean stopThread = true;

public void stopRun(){
    this.stopThread=false;
}

public UDPreceive(p2p_app p2p){
    this.p2p = p2p;
}

/**
 * @param args
 */

public void run(){
    DatagramSocket socket=null;
    UDPpacket udp;
    byte[] x = new byte[1000];
    try{
        socket = new DatagramSocket(8734);
        socket.setBroadcast(true);
        //while(this.p2p.getExit() !=-1){
        while(stopThread){  
            p = new DatagramPacket(x,x.length);
            socket.receive(p);

            udp = new UDPpacket(p,this.p2p);
            udp.tostring();
            //udp.setDatagramPacket(p);

            //String d = new String(p.getData());
            //System.out.println("Mensagem enviada por mim: "+d);
        }
        //Thread.sleep(100);
    } catch (SocketException e) {
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

}

我如何能够在 p2p_app 类中杀死我的主函数上的线程?我为我的 UDPreceiver 类创建了一个线程:F

4

4 回答 4

3

在大多数情况下,杀死线程的唯一“安全”方法是对线程进行编码,使其可以接收到停止信号。例如,使用一个名为的布尔变量shouldQuit并让线程定期检查该变量,如果为真则退出。你也可以做一些事情,比如中断线程,但这并不总是安全的。

于 2013-04-24T21:47:25.337 回答
2
package test;

/**
 * simple thread class that prints '.' to the screen
 * @author Manex
 *
 */
public class ThreadTest extends Thread {
private boolean running = true ;

public void run(){
try{
    while(running){
        //do something here
        System.out.print(".");
        sleep(1000);
    }
}catch(InterruptedException e){
    System.out.println(e.getMessage());
}
    System.out.println("Stopped");
}
/**
 * a method to stop the thread
 */
public void stopRunning(){
    this.running = false ;
}

public static void main(String[] args){

    //creating threads
    ThreadTest[] t = new ThreadTest[2] ;
    t[0] = new ThreadTest() ;
    t[1] = new ThreadTest() ;

    //starting threads
    for(ThreadTest e : t){
        e.start();
    }
    try {
        //the main thread does something
        int x = 5 ;
        while(x > 0){
            sleep(1000) ;
            x -= 1 ;
        }
        //the main thread ended
        System.out.println("main thread ends here");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    //before exiting - stop all threads 
    for(ThreadTest e : t){
        e.stopRunning();
    }
}
} 

如果您出于任何原因计划停止您创建的线程,您应该继续跟踪它们并保存对您可能想要停止的每个线程的引用,而不是等待它自己完成执行(运行方法简单地结束) . 在这个简单的测试中,如果您删除停止循环,线程将继续打印并且永远不会停止,直到您手动停止它们,即使在主线程终止之后也是如此。我希望这很有用..

于 2013-04-24T22:33:23.013 回答
0

最后,我做到了!结果和你们说的有点不一样,我会发布我的答案,以防有人问!

解决方案是向我自己发送一个 DatagramPacket !:)

类 udpReceiver ->

//package testeThread;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

public class udpReceiver implements Runnable {
public volatile boolean stopThread = true;
private DatagramSocket socket;

//private DatagramSocket socket;

public udpReceiver() {
    // TODO Auto-generated constructor stub
    //this.socket = socket;
}

public void stopRun(){
    synchronized(this){

        this.stopThread=false;
        byte[] x = new byte[1000];
        try{
            DatagramPacket p = new DatagramPacket(x,x.length,InetAddress.getLocalHost(),8737);
            this.socket.send(p);
        } catch(UnknownHostException e){
            e.printStackTrace();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}

/**
 * @param args
 */
public void run(){
     //DatagramSocket socket=null;
     DatagramPacket p = null;
     //byte[] x = new byte[1000];
     try{
     this.socket = new DatagramSocket(8737);
     this.socket.setBroadcast(true);
     }catch(SocketException e){
         e.printStackTrace();   
     }
        //socket.setSoTimeout(5*1000); // 20 seconds
     while(stopThread){
            byte[] x = new byte[1000];
            p = new DatagramPacket(x,x.length);
            try{
            this.socket.receive(p);
            }catch(IOException e){
                e.printStackTrace();
            }

            String d = new String(p.getData());
            System.out.println("Mensagem enviada por mim: "+d);
     }
     this.socket.close();
     /* try{
        socket = new DatagramSocket(8735);
        socket.setBroadcast(true);
        //socket.setSoTimeout(5*1000); // 20 seconds
        while(stopThread){
            byte[] x = new byte[1000];
            p = new DatagramPacket(x,x.length);
            try {
              socket.receive(p);
            } catch (SocketTimeoutException e) {
              if (stopThread){
                //System.err.println("Warning: socket timed out before program completed: " + e.getLocalizedMessage());
              } else {
                // program completed, so just ignore and move on
                break;
              }
            }

            String d = new String(p.getData());

            //System.out.println("Mensagem enviada por mim: "+d);
            //System.out.println("SOCKET CLOSE"+socket.isConnected());
        }
        //socket.setSoTimeout(1000);
        socket.close();
        System.out.println("SOCKET CLOSE"+socket.isConnected());

    } catch (SocketException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException io){
        io.printStackTrace();
    }*/
    /*catch (SocketTimeoutException soc){
        if(this.stopThread == false) {
            this.stopThread = false;
        }
        soc.printStackTrace();
    }*/
}

}

类服务器->

import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Scanner;

//package testeThread;

public class Servidor {

private int exit;

public Servidor() {
    // TODO Auto-generated constructor stub
}

public int getExit(){
    return this.exit;
}

public void setExit(int exit){
    this.exit = exit;
}

public int choiceMenu(int i){
    int numRowsInConsole = 60;
    final String ESC = "\033[";

    switch(i){
    case 1:
        System.out.println("FUNCIONOU HELLO");
        System.out.print(ESC + "2J");
        /*for (int ii=0; ii<numRowsInConsole; ii++) {
            // scroll down one line
            System.out.println("");
        }*/
        break;
    case 2:
        System.out.println("FUNCIONOU VASCO");
        System.out.print(ESC + "2J");
        break;
    case 3:
        i=-1;
        System.out.print(ESC + "2J");
        break;
    default:
    }
    return i;
}

public void menu(){
    System.out.println("1:Hello");
    System.out.println("2:");
    System.out.println("3:Exit");
}

@SuppressWarnings("deprecation")
public static void main(String[] args) {
    int i;
    Servidor s = new Servidor();

    //try{
        //DatagramSocket socket = new DatagramSocket(8735);
        udpReceiver udpR = new udpReceiver();
        Thread t = new Thread(udpR);
        t.start();

        Scanner sc = new Scanner(System.in);
        while(s.getExit() != -1){
            s.menu();
            i = sc.nextInt();
            s.setExit(s.choiceMenu(i));
            System.out.println(s.getExit());
        }
    //DatagramSocket socket = new DatagramSocket(8735);

    //socket.close();
    //t.interrupt();
    udpR.stopRun();

    try{
    t.join();
    }catch(InterruptedException e){
        e.printStackTrace();
    }
    System.out.println("MAIN FIM");
    //t.stop();
    /*}catch(SocketException e){
        e.printStackTrace();
    }*/



}

}

PS:那个版本和楼上那个不一样……但是我得到了和我写另一个一样的逻辑,现在它工作得很好!我可以在不使用 CTRL+C 的情况下退出程序,它现在可以接收消息了!:)

于 2013-04-25T18:11:05.423 回答
0

您的 UDPReceive 线程没有停止的原因是您在 UDPReceive.run() while 循环中使用了阻塞方法DatagramSocket.receive()。此方法的 JavaDocs 说:“此方法阻塞,直到收到数据报。” 块意味着永远不会返回。因此,当您希望程序退出时,带有 receive() 的线程仍在运行。它是一个“挂起的线程”,解除它的唯一方法是杀死整个进程,例如 Ctrl+C。

要修复它,请在 UDPReceive.run() while 循环开始之前调用socket.setSoTimeout() 。这将使对 receive() 的最终调用超时并实际完成。然后捕获如果您的套接字超时将发生的SocketTimeoutException(例如,在程序结束时线程完成时,或者如果您有实际超时错误条件更早)并适当地处理异常(例如,如果触发 stopThread 只需忽略异常,或者如果 stopThread 尚未触发,则将其记录为警告)。例子:

public void run(){
    DatagramSocket socket=null;
    UDPpacket udp;
    byte[] x = new byte[1000];
    try{
        socket = new DatagramSocket(8734);
        socket.setBroadcast(true);
        socket.setSoTimeout(20*1000); // 20 seconds
        //while(this.p2p.getExit() !=-1){
        while(stopThread){  
            p = new DatagramPacket(x,x.length);
            try {
              socket.receive(p);
            } catch (SocketTimeoutException e) {
              if (stopThread){
                System.err.println("Warning: socket timed out " +
                    "before program completed: " + e.getLocalizedMessage());
              } else {
                // program completed, so just ignore and move on
                break;
              }
            }

            udp = new UDPpacket(p,this.p2p);
            udp.tostring();
            //udp.setDatagramPacket(p);

            //String d = new String(p.getData());
            //System.out.println("Mensagem enviada por mim: "+d);
        }
        //Thread.sleep(100);
    } catch (SocketException e) {
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

这应该可以解决问题。stopThread 逻辑本身看起来不错(尽管我会将布尔值重命名为 continueThread,因为当它为false时您将停止)。

于 2013-04-25T13:26:54.890 回答