» »

[JAVA] zaustavitev niti (threadov)

[JAVA] zaustavitev niti (threadov)

l0g1t3ch ::

Kako pravilno ustavim/ubijem nit v javi. Metoda stop je kolkr gledam neprimerna in je odsvetovana.
Je torej torej uredu če prekinem neskončno zanko ki teče v niti in potem odstranim referenco na to nit pa jo bo potem že GC spucal ?
Kaj pa join metoda, mi kdo lahko po kmečko razloži kaj le ta nardi.

Hvala
  • spremenilo: snow ()

PaX_MaN ::

Dodaš en boolean v thread, in ko je ta boolean false (oz. true, kakor gledaš) returnaš iz threada.
Link, Link 2

jan01 ::

l0g1t3ch ::


import java.net.*;
import java.io.*;

public class ChatClient implements Runnable
{  	
	protected Socket socket;
	protected Thread thread = null;
	protected BufferedReader  console;
	protected PrintWriter out;
	protected ChatClientThread client;
	private boolean run = true;

	public ChatClient(String serverName, int serverPort)
	{  
		try
		{  
			socket = new Socket(serverName, serverPort);
			System.out.println("Connected: " + socket + "\n");
			this.start();
		}
		catch(UnknownHostException uhe)
		{  
			System.out.println("Unknown host: " + uhe); 
		}
		catch(IOException ioe)
		{  
			System.out.println("IO error: " + ioe); 
		}
	}
	
	public void start() throws IOException
	{  	
		console = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
		
		if (thread == null)
		{  
			client = new ChatClientThread(this, socket); //podamo instanco ChatClient-a 
			thread = new Thread(this);   //ustvarimo nov Thread objekt iz ChatCLienta                
			thread.start(); //ter ga poženemo, prične se izvajat run metoda
		}
	}
	
	public void run()
	{  
		while(run)
		{  
			try
			{  
				String msg = console.readLine();
				if(msg.trim().equals("EXIT"))
				{
					stop();
					client.close();
					client.myStop();
				}
				else
				{
					out.println(msg); //berem iz konzole, posiljam na server
					out.flush();
				}
			}
			catch(IOException ioe)
			{ 
				System.out.println("IO error: " + ioe);
				stop();
			}
		}
		return;
	}
		
	public void stop()
	{  
		run = false;
		client.myStop();
		
		try
		{	
			console.close();
			out.close();
		}
		catch(IOException ioe){}	

		System.exit(0);	
	}
	
	public static void main(String args[])
	{  
		if (args.length != 2)
		{
			System.out.println("Usage: java ChatClient host port");
			System.exit(1);
		}
		else
		{
			try
			{
				ChatClient chat_client = new ChatClient(args[0], Integer.parseInt(args[1]));
			}
			catch (NumberFormatException nfe) 
			{
				System.out.println("Parse error: " + nfe); 
			}
		}
			
	}
}


//********************************************



import java.net.*;
import java.io.*;

public class ChatClientThread extends Thread
{  
	protected Socket socket;
	protected ChatClient client;
	protected BufferedReader in;
	private boolean run = true;
	
	public ChatClientThread(ChatClient client, Socket socket)
	{  
		this.client = client;
		this.socket = socket;
		this.open();  
		this.start(); //klice run metodo ChatClientThread
	}
	
	public void open()
	{  
		try
		{ 
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		}
		catch(IOException ioe)
		{  
			System.out.println("IO Error: " + ioe);
			client.stop();
		}
	}
	
	public void close()
	{  
		try
		{  
			if (in != null) in.close();
			if (socket != null) socket.close();
			run = false;
		}
		catch(IOException ioe)
		{  
			System.out.println("IO Error: " + ioe);
		}
	}
	
	public void run()
	{  
		while(run)
		{  
			try
			{  
				System.out.println( in.readLine() ); //pisem v konzolo kar dobim iz serverja
			}
			catch(IOException ioe)
			{  
				System.out.println("IO Error: " + ioe);
				client.stop();
				myStop();
			}
		}
		return;
	}
	
	public void myStop()
	{
		run = false;
	}
}



Kaku tukaj pravilno zapreti vse tokove in ustaviti niti ? Karkoli delam zmeraj ko končam program torej ko napišem exit se mi program sicer konča ampak javi IOEception

morbo ::

Ker se v tvoji kodi res ne znajdem (ne da se mi :P ) ti prilepim en preprost serverček, mogoče pa ti pride prav. Za vsako vtičnico ustvari novo nit in jo po prejemu ukaza exit tudi lepo zapre. Edini problem predstavlja glavna nit, ki je v bistvu neskončna zanka in se je ne da lepo zaustavit (ServerSocket.accept() blokira nit v kateri se izvaja, tako da preverjanje odpade ). To lahko rešiš tako da server poženeš v nekem preprostem GUI-ju, kot daemon thread - torej nit kjer izvajaš ServerSocket.accept() naj bo daemon. Ko boš zaprl GUI, se bo zaustavila tudi ta nit.

Na server se priključi s telnetom


package simpleserver;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;

/**
 *
 */
public class SimpleServer {
    
    // seznam handlerjev vtičnic
    private ArrayList<ClientHandler> handlerji = new ArrayList<ClientHandler>();
    /*
     * Konstruktor
     */
    public SimpleServer() throws IOException {
        
        try {
            ServerSocket ss = new ServerSocket(4999);
            int id = 1;
            while (true) {
                Socket s = ss.accept();
                ClientHandler handler = new ClientHandler(s, id);
                new Thread(handler).start();
                synchronized (handlerji) {
                    handlerji.add(handler);
                }
                poslji(id + " se je prikljucil na server.", handler);
                id++;
            }
        } catch (IOException ex) {
            throw ex;
        }
    }
    /**
     * poslje sporocilo v vse vtičnice, razen pošiljateljeve
     */
    private synchronized void poslji(String sporocilo, ClientHandler posiljatelj) {
        
        for (ClientHandler hand : handlerji) {
            if (hand == posiljatelj)
                continue;
            hand.pisi(sporocilo);
        }
    }
    
    /**
     * Razred ClientHandler skrbi za branje iz vtičnice in razpošiljanje prebranega
     * ostalim handlerjem
     */
    class ClientHandler implements Runnable {
        
        private final Socket vticnica;
        private final int id;
        private PrintWriter pisec;
        
        /*
         * Konstruktor
         */
        public ClientHandler(Socket s, int id) throws IOException {
            
            this.vticnica = s;
            this.id = id;
            pisec = new PrintWriter(vticnica.getOutputStream(), true);
        }
        
        public void run() {
            
            try {
                pisi("Pozdravljen/a! Za izhod vpisi ukaz EXIT.\n");
                Scanner bralec = new Scanner(vticnica.getInputStream());
                try {
                    //System.out.println("Zaganjam " + this.id + "-o vtičnico");
                    boolean zakljuci = false;
                    // dokler skozi vtičnico ne dobimo ukaza exit, beremo vrstice
                    // teksta, ki ga razpošljemo vsem ostalim vtičnicam.
                    while (!zakljuci && bralec.hasNextLine()) {
                        
                        String sporocilo = bralec.nextLine();
                        if (sporocilo.trim().equalsIgnoreCase("exit")) {
                            zakljuci = true;
                            sporocilo = id + " je zaprl povezavo.";
                        }
                        poslji(id + ": " + sporocilo, this);
                    }
                } finally {
                    // handlerja izločimo iz seznama in zapremo resurse
                    synchronized (handlerji) {
                        handlerji.remove(this);
                    }
                    bralec.close();
                    pisec.close();
                    vticnica.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        /**
         * zapiše sporočilo v vtičnico
         */
        protected void pisi(String sporocilo) {
            
            pisec.println(sporocilo);
        }
    }
    
    public static void main(String[] args) {
        
        try {
            new SimpleServer();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    
}

l0g1t3ch ::

Hvala za trud ampak server sm spisu že sam in lavfa kot namazan. Tista koda ki sm jo prilepu je za clienta, ki sicer tudi lepo deluje samo ob zaustavitvi zmerej vrže vn eno izjemo pa se nikakor ne morm znebit tistega.

Sergio ::

morbo: Če ne želiš da bi ti ServerSocket blokiral, ima Java non-blocking I/O že implementiran v java.nio paketu.
Tako grem jaz, tako gre vsak, kdor čuti cilj v daljavi:
če usoda ustavi mu korak,
on se ji zoperstavi.

morbo ::

Sergio, java.nio poznam in sem že neki delal.

l0g1t3ch, kodo sem prilimal tudi zato ker prvotno vprašanje zadeva prekinjanje niti in čiščenje resursov. Kje pa ti vrže izjemo? A tale klient je mišljen da bo tekel v konzoli ali grafično?

morbo ::

Spovnil sem se da k zgornjemu serverju spada zraven tudi client, ker sem nekoč jst to že delal...:D

l0g1t3ch, tvoje koda se mi zdi way to much zakomplicirana. Bom poiskal, pa priflikam...

morbo ::

l0g1t3ch,

izjemo ti najbrž javlja, ker prevečkrat kličeš stop() metode obeh razredov(dvosmerno! - iz enega v drugega in obratno). Dva javna razreda sta povsem nepotrebna.

klient za zgornji server:

package simpleclient;

import java.awt.EventQueue;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
 *
 */
public class SimpleClient implements Runnable {
    
    private BufferedReader socketIn, reader;
    private PrintWriter socketOut;
    private Socket soc;
    private boolean globalExit = false;
    /*
     * Konstruktor
     */
    public SimpleClient(String host, int port) throws IOException {
        
        soc = new Socket(host, port);
        socketOut = new PrintWriter(soc.getOutputStream(), true);
        socketIn = new BufferedReader(new InputStreamReader(soc.getInputStream()));
        reader = new BufferedReader(new InputStreamReader(System.in));
        new Thread(this).start();
        /*
         * Branje iz konzole in pisanje v vtičnico naložimo kar event dispatcher niti.
         */
        EventQueue.invokeLater(new Runnable() {
            
            public void run() {
                
                try {
                    while(!globalExit) {
                        String msg = reader.readLine();
                        if (msg.trim().equalsIgnoreCase("exit")) {
                            globalExit = true;
                        }
                        socketOut.println(msg);
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });
    }
    /*
     * kar preberemo iz vtičnice, zapišemo v konzolo
     */
    public void run() {

        try {
            try {
                while(!globalExit) {
                    System.out.println(socketIn.readLine()); 
                }
            } finally {
                // počistimo
                socketIn.close();
                socketOut.close();
                reader.close();
                soc.close();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    /*
     * Main
     */
    public static void main(String args[]) {
        
        try {
            if (args.length < 2)
                new SimpleClient("localhost", 4999);
            else
                new SimpleClient(args[0], Integer.parseInt(args[1]));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

l0g1t3ch ::

Spet jaz :D

Zanima me kako narediti java program ki bi poganjal več takih serverjev kot je zgoraj. Recimo da en server predstavlja en kanal kot kanal na ircu. Recimo program izvaja vsak gornji server kot nit.

Sam sem se nekaj igral z svojo verzijo chat serverja in stvar približno deluje. Požene dve niti objekta Room ki predstavljata vsaka svoj kanal in zaenkrat delujeta vsaka na svojem portu.
Problem je samo v tem da kar napišem v enem klientu ki je povezan na prvo nit(je v prvi sobi) dobi tudi drugi klient ki je povezan na drugo nit(je v drugi sobi)

Problem je verjetno v temle
private static Vector<ClientHandler> activeThreads; 

To je seznam vseh clientHandlerjev torej niti ki v končni fazi komunicirajo z clienti. Definiran je znotraj razreda Room in je mišljeno da ima do tega seznama dostop samo tisti ki se poveže na določeno sobo.

V praksi naj bi zgledal tkole, če poženeš Server na portu 8000 potem naj bi prva nit Room poslušala na portu 8000 druga nit pa na portu 8001
In uporabniki ki se povežejo prek porta 8000 lahko komunicirajo samo med sabo torej uporabniki povezani prek porta 8001 ne dobivajo tega kar pišejo.

Ampak meni gre to vsepovprek. Kdorkoli kaj napiše dobijo vsi. Pa nevem zakaj sej je seznam definiran kot privatni znotrej tistega razreda, ki predstavlja potem sobo


import java.util.*;
import java.io.*;
import java.net.*; 

public class Server 
{
	public static void main(String[] args) 
	{
		if (args.length != 1)
		{
			System.out.println("Usage: java Server port");
			System.exit(1);
		}	
		else
		{
			Room soba = new Room( Integer.parseInt(args[0]), "soba" );
			soba.start();
			
			Room soba2 = new Room( Integer.parseInt(args[0])+1 , "soba2" );
			soba2.start();
			
			
		}
		

	}
	
}

class Room extends Thread 
{
	private static Vector<ClientHandler> activeThreads; 
	protected int i;
	protected int port; 
	
	public Room(int p, String name)
	{
		System.out.println("Chat server started."); 
		activeThreads = new Vector<ClientHandler>(); 
		i = 1; 
		port = p;
	}

	public static void removeThread( ClientHandler nit )
	{
		activeThreads.removeElement(nit); 	
	
	}
	
	public static Iterator getIterator()
	{
	    return activeThreads.iterator(); 
	}
	
	
	public void run()		
	{
			try 
			{
				ServerSocket s = new ServerSocket( port ); 
				while(true) 
				{
					Socket incoming = s.accept(); 
					System.out.println("Spawning client thread " + i);
					ClientHandler newThread = new ClientHandler(incoming, i); 
					activeThreads.addElement(newThread); 
					newThread.start(); 
					i++; 
				}
			} 
			catch (NumberFormatException nfe) 
			{
				System.out.println("Parse error: " + nfe); 
			}
			catch (Exception e) 
			{
				System.out.println("Error: " + e); 
			}
	}
}

class ClientHandler extends Thread
{
	protected Socket incoming;  
	protected int id; 
	protected String nick;
	protected BufferedReader in; 
	protected PrintWriter out;
 
	//konstruktor
	public ClientHandler(Socket incoming, int id) 
	{
		this.incoming = incoming; 
		this.id = id; 
		try 
		{
			in = new BufferedReader(new InputStreamReader(incoming.getInputStream())); 	    
			out = new PrintWriter(new OutputStreamWriter(incoming.getOutputStream())); 	 

			putMessage("Hello! This is simple java chat server. Enter \"EXIT\" to end chat.");   
			putMessage("Enter your nickname: "); 
			nick = in.readLine(); 
			putMessage("Welcome " + nick + ", now you can start chating");
			putMessage("****************************************");
			
		} 
		catch (IOException ioe) 
		{
			System.out.println("IO Error: " + ioe); 
		}
	}

	public synchronized void putMessage(String msg) 
	{
		if(out != null) //preverimo če izhodni tok obstaja, ker metodi ne mečeta izjem
		{
			out.println(msg); 
			out.flush();
		}
	}

	public void run() 
	{
		System.out.println("Client handler " + id + " started."); 
		if (in != null && out != null) 
		{		
			try 
			{
				while(true) 
				{
					String str = in.readLine(); 
					if (str == null) 
					{
						break; 
					} 
					else 
				    {
						System.out.println("Received (" + id + "): " + str);

						if (str.trim().equals("EXIT")) 
						{
							break; 
						} 	
						else 
						{
							Iterator iter = Room.getIterator();
							while  (iter.hasNext()) 
							{
								ClientHandler t = (ClientHandler)iter.next(); 
								if (t != this)
								{
									t.putMessage(nick + ": " + str); 
								}
							}
						}
					}
				}
				incoming.close(); 
			//	Room.activeThreads.removeElement(this);
				Room.removeThread(this);
			} 
			catch (IOException ioe) 
			{
				System.out.println("IOE Error: " + ioe); 
			}
		}
		System.out.println("Client thread " + id + " stopped."); 
		return;
	}	
}

morbo ::

Aha, aplikacija se debeli... :D

Logika ena soba, en server je zgrešena - server (torej port) naj bo le en, saj ni smotrno da se uporabniki nonstop priklapljajo / odklapljajo od serverja, če hočejo menjati sobo (pa tudi izvedba bi bila precej težka - kako naj uporabnik ve za vse aktivne porte?). Postopek bi bil recimo tak: Priklop na server -> server vrne seznam sob -> uporabnik se prijavi v sobo... 'Soba' naj ne bo nič drugega kot seznam ene gruče uporabnikov, oz. nek razred, ki povezuje rezrede 'ClientHandler' in sinhronizira komunikacijo med njimi.

l0g1t3ch ::

Vem da ta pristop ni najbolši in sem tudi sam premišljal o pristopu kot ga omenjaš ti. Vendar ko sm že neki naredu me vseeno zanima kako to pripravit do delovanja oz kaj delam narobe da vsi vidijo vse.

morbo ::

private static Vector activeThreads;

stvar ne dela ker je tole static - to pomeni da si vsi predmeti (=sobe) delijo isti izvod spremenljivke.

l0g1t3ch ::

eh ja kera bedna napaka
Maš drugače kak link kje so dobr razloženi ti access modifier-ji oz če se teb da to mal po kmečk povedat

morbo ::

Dosegljivost 'članov' (members) razreda (člani so atributi, metode, vgnezdeni razredi in vgnezdeni vmesniki):

private
- član je dosegljiv le v razredu, kjer je najavljen (in v njegovih notranjih razredih)
<brez modifierja>
('package-private') - dosegljiv iz vseh razredov v istem paketu
protected
- dosegljiv iz podrazredov razreda kjer je najavljen in iz vseh razredov v istem paketu
public
- dosegljiv od povsod

Statične člane pa si delijo vsi izvodi razreda kjer so ti definirani, zato so dosegljivi brez izvoda predmeta, ampak zanje veljajo ista pravila za dostop.

V tvojem primeru seznam handlerjev ne sme bit statičen, ker si ga predmeti delijo (vsi izvodi razreda Room obdelujejo isto kopijo). To da je private, pomeni le da ni dostopen od zunaj (iz drugih razredov).

l0g1t3ch ::

Aha no jest sm mislu d private prepreč d bi drugi dostopal do tega. Hvala za pojasnilo

l0g1t3ch ::

Sam tukaj če pobrišem static mi potem zadeva ne dela, ker pravi da do nestatične spremenljivke nemorem dostopati iz statičnega konteksta.
Sam v ClientHandler razredu nimam ničesar statičnega samo predvidevam da je metoda run() statična saj je v končni fazi nekaj podobnega kot main() metoda.

Se da ta problem kako rešit in kako ?

morbo ::

Seveda teži, saj imaš v Room dve statični metodi, ki kličeta 'activeThreads' (getIterator() in removeThread()) - iz teh metod ne moreš klicat nestatičnih spremenljivk, saj so slednje spremenljivke stanja (instance), statične metode pa ne vedo nič o instancah predmeta.

Pobriši static in razredu ClientHandler pošlji referenco na Room, ki ga je ustvarila. Potem te metode kliči s pomočjo reference.

metoda run() ni statična - saj ni definirana kot taka! :P

l0g1t3ch ::

Server.java:141: non-static variable activeThreads cannot be referenced from a static context
Iterator iter = Room.activeThreads.iterator();
^
Server.java:154: non-static variable activeThreads cannot be referenced from a static context
Room.activeThreads.removeElement(this);


Tole dobim za error. Sem pa naredu tako da je
protected  Vector<ClientHandler> activeThreads; 

Tako ne rabim tistih dveh metod getIterator() in removeThread().
Če v to kodo dodam besedico static potem dela sam je že prej omenjena težava. Če pa pobrišem static mi pa vrže zgornji dve napaki pa nevem zakaj. Ker tam v kodi ni nič static vsaj ne da bi vedel ;(( Edina kar je še static je Main() metoda

Sem pa poskušal tudi te dve metodi getIterator() in removeThread(). obdržati pa ju narest nestatični pa ista napaka.

Kaj je tle statičnega d javlja to :(

morbo ::

Glej, čisto preprosto je:

najprej pobriši static metodam in seznamu, ki sem jih omenil. Potem konstruktorju handlerja dodaj nov parameter tipa Room, shrani ga v private spremenljivko. Ob stvarjenju novih predmetov handler, pošlji konstruktorju referenco 'this' (torej referenco na Room, ki ga je ustvarila). Metodi getIterator() in removeThread() lahko zdaj kličeš preko te reference in ne statično.

Statične metode lahko kličeš kar direktno, brez predmetne spremenljivke:
 Room.getIterator() 

Medtem ko za ostale rabiš objekt:
Room soba = new Room()
soba.getIterator()

Ti moraš vsakemu handlerju poslat sklic na objekt Room - na isti način kot mu pošlješ Socket in id

ps:
protected modifier je namenjen dedovanju - če hočeš paketni dostop najavi predmet brez modifierja

l0g1t3ch ::

Sm naredu tko in dela. Me pa še zmer zanima zakaj je prej javljal tist glede statičnega dostopa če pa ni blo nič kar bi blo static

morbo ::

To pa je zato ker si do spremenljivke še vedno dostopal kot da bi bila statična


Vredno ogleda ...

TemaSporočilaOglediZadnje sporočilo
TemaSporočilaOglediZadnje sporočilo
»

[JAVA] HTTPS client

Oddelek: Programiranje
173030 (1760) peterv6i
»

[Java] Multi Client chat server

Oddelek: Programiranje
262327 (1598) javaMaster
»

Java - problem povezave na FTP

Oddelek: Programiranje
101081 (938) igor0203
»

[Delphi] client server

Oddelek: Programiranje
61181 (1040) jvolk
»

Kako pošiljanje datotek v JAVI.

Oddelek: Programiranje
141724 (1595) kopernik

Več podobnih tem