Как убить нить на java?

У меня головная боль при убийстве потока с помощью java ... Я видел много тем о stackoverflow, и я не заставил их работать над моим кодом ... Может кто-нибудь объяснить мне, как я могу убить поток без использования устаревшего функция (например, стоп) и безопасным способом (также мой поток использует сокет: 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


person Damiii    schedule 24.04.2013    source источник
comment
Кодируйте поток только для того, чтобы выполнять ту работу, которую вы хотите, и для завершения самого себя, когда для него нет работы. Тогда вам не нужно будет тянуться извне, чтобы остановить это.   -  person David Schwartz    schedule 25.04.2013
comment
Что ж, Java - медленно меняющийся язык, поэтому новые ответы, скорее всего, будут старыми. Что пробовали и чем не получилось?   -  person madth3    schedule 25.04.2013
comment
Я попробовал: public volatile boolean stopThread = true; public void stopRun () {this.stopThread = false; } так как я пробовал также прерванную функцию для потока .. И оба еще не работали   -  person Damiii    schedule 25.04.2013
comment
ну, я видел эту статью до того, как написал свой вопрос, и она не решила его ...   -  person Damiii    schedule 25.04.2013
comment
Значит, вы неправильно применяете концепции, но концепции в этой ссылке верны и, как вы видите, по сути совпадают с ответами, которые вы здесь получаете.   -  person Hovercraft Full Of Eels    schedule 25.04.2013
comment
Причина, по которой ваш поток UDPReceive не останавливается, заключается в том, что вы используете метод блокировки DatagramSocket.receive () в своем цикле UDPReceive.run () while. В JavaDocs для этого метода говорится: Этот метод блокируется до тех пор, пока не будет получена дейтаграмма. Под блоками это означает, что никогда, никогда не вернется. Таким образом, поток с receive () все еще работает, когда вы хотите, чтобы ваша программа завершилась. Это зависший поток, и единственный способ избавиться от него - убить весь процесс, например Ctrl + C. Так ? я сделал то же самое, что было сказано в твоей статье! Проблема не в этом ... дело в другом ...   -  person Damiii    schedule 25.04.2013
comment
@HovercraftFullOfEels, как видите, ответ был неправильным! ;) Я не неправильно сформулировал концепцию! ;)   -  person Damiii    schedule 26.04.2013


Ответы (4)


По большей части, единственный «безопасный» способ убить поток - это закодировать поток таким образом, чтобы он мог получить сигнал об остановке. Например, используйте логическую переменную с именем shouldQuit и пусть поток периодически проверяет эту переменную, завершая работу, если она истинна. Вы также можете делать такие вещи, как прерывание потока, но это не всегда безопасно.

person KyleM    schedule 24.04.2013
comment
Что ж, в моем примере выше я попробовал этот метод, и он, похоже, не работает: f По крайней мере, когда я пытаюсь закрыть приложение в моей командной строке, оно не закрывается .. я должен нажать CTRL + C, чтобы выйти из него .. - person Damiii; 25.04.2013
comment
@ user2317427 Вы сказали, что это программа на Java. Вам нужно будет установить shouldQuit = true из другого потока Java. Можете ли вы отредактировать исходный пост и более полно объяснить, как вы собираетесь выйти из обсуждения? - person KyleM; 25.04.2013
comment
@ user2317427 Также ctrl + c реализован в большинстве операционных систем для отправки прерывания. Поэтому, когда вы набираете ctrl + c, текущая выполняемая программа / поток будет прервана, передав управление обратно операционной системе, которая затем убьет вашу программу. Этот процесс не зависит от Java ... \ - person KyleM; 25.04.2013
comment
Что ж, ожидание таково: в моем классе p2p_app есть функция Main, и то, что я хочу сделать с моей основной функцией, это: запустить поток для моего класса UDPreceiver, выполнить оператор while, если оператор while закончился = ›kill все мои темы и выйдите из программы. - person Damiii; 25.04.2013
comment
@ user2317427 Кроме того, если вы хотите дождаться завершения выполнения потоков, вы можете использовать метод Thread.join (например, вы можете вызвать Thread.join для каждого потока в массиве потоков из вашего основного метода). - person KyleM; 25.04.2013

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();
    }
}
} 

если вы планируете остановить потоки, которые вы создали, по какой-либо причине, вы должны продолжать отслеживать их и держать ссылку на каждый поток, который вы, возможно, захотите остановить, вместо того, чтобы ждать, пока он сам выполнит выполнение (метод запуска просто заканчивается) . в этом простом тесте, если вы удалите цикл остановки, потоки продолжат печать и никогда не остановятся, пока вы не остановите их вручную, даже после завершения основного потока ... Надеюсь, это было полезно ...

person user2317441    schedule 24.04.2013
comment
почему вы используете sleep (1000) при run ()? - person Damiii; 25.04.2013
comment
sleep (1000): переводит текущий поток в режим ожидания (ожидание) 1000 миллисекунд - person user2317441; 25.04.2013
comment
да, но нужно ли это? - person Damiii; 25.04.2013
comment
иногда это полезно ... в этом случае я использовал его, чтобы имитировать что-то. в реальном мире поток будет суетливо ждать клиентского соединения / сообщения / ввода или выполнять какой-то большой блок кода. - person user2317441; 25.04.2013

Причина, по которой ваш поток UDPReceive не останавливается, заключается в том, что вы используете метод блокировки DatagramSocket.receive () в вашем цикле UDPReceive.run () while. В JavaDocs для этого метода говорится: «Этот метод блокируется, пока не будет получена дейтаграмма». Под блоками это означает, что никогда, никогда не вернется. Таким образом, поток с receive () все еще работает, когда вы хотите, чтобы ваша программа завершилась. Это «зависший поток», и единственный способ избавиться от него - убить весь процесс, например Ctrl + C.

Чтобы исправить это, вызовите socket. setSoTimeout () перед запуском цикла UDPReceive.run () while. Это приведет к последнему вызову метода 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).

person Will    schedule 25.04.2013
comment
как я могу сделать эту часть, если срабатывает stopThread, просто игнорируйте исключение, или если stopThread еще не запущен, зарегистрируйте его как предупреждение). ? Игнорирование исключения? Если Socket.setSoTimeout () выполнен, мой поток уже отключен, и я не могу вернуться ... по крайней мере, из того, что я пробовал, я получил это ... - person Damiii; 25.04.2013
comment
Хорошо, см. Мой обновленный ответ с образцом кода. И, пожалуйста, примите, если это сработает для вас. ;) В общем, хорошая практика - знать об использовании блокирующего и неблокирующего ввода-вывода и планировать возможные тайм-ауты. Кроме того, обрабатывайте исключения в соответствии с вашим вариантом использования; мой просто пример. - person Will; 25.04.2013
comment
Хорошо, теперь это сработало, я могу выйти из программы, не используя CTRL + C! Но я не могу получить свой пакет датагрампакетов сейчас ... лол, когда я смог получить их раньше лол - person Damiii; 25.04.2013
comment
Как вы обнаружили в своем ответе, мое решение будет работать только для приема пакетов до тех пор, пока их больше не будет. В исходном коде вы не отправляли никаких пакетов. Я предполагал, что вы добавите это позже ... что вы и сделали. ;) - person Will; 25.04.2013
comment
ну, тот, который я не отправлял, но на моем другом компьютере я написал код, чтобы отправить мне несколько пакетов! :) - person Damiii; 26.04.2013

Наконец-то у меня это получилось! Результат немного отличался от того, что вы сказали, я отправлю свой ответ, если кто-то его спросит!

Решением было отправить мне пакет 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();
    }*/



}

}

P.S: эта версия не такая, как та, что наверху ... Но у меня та же логика, что и у другой, и теперь она работает хорошо! Я могу выйти из программы, не используя CTRL + C, и теперь она получит сообщение! :)

person Damiii    schedule 25.04.2013
comment
Хех, да, вы не получите никаких пакетов, если они не будут отправлены. - person Will; 25.04.2013
comment
на самом деле, у меня есть два компьютера, один, который отправляет пакеты, и мой, который получает! С этим кодом я мог бы получить код с другого моего компьютера, а также выйти из программы, не используя CTRL + C! :) - person Damiii; 26.04.2013