Java Hilos
Java Hilos
Rodrigo Santamara
Hilos
run()
Los hilos implementan prioridad y mecanismos de sincronizacin Cualquier clase se puede hacer hilo
finalizacin return
implements Runnable
Hilos
public class PingPong extends Thread { private String word; public PingPong(String s) {word=s;} public void run() { for (int i=0;i<3000;i++) {System.out.print(word); System.out.flush();} } public static void main(String[] args) {Thread tP=new PingPong("P"); Thread tp=new PingPong("p"); //tP.setPriority(Thread.MAX_PRIORITY); //tp.setPriority(Thread.MIN_PRIORITY); tp.start(); tP.start(); } }
Clase Thread
Implementa Runnable
wait(), notify()
http://download.oracle.com/javase/tutorial/essential/concurrency
Sincronizacin
Los hilos se comunican generalmente a travs de campos y los objetos que tienen esos campos
Es una forma de comunicacin eficiente Pero puede plantear errores de interferencias entre hilos
La sincronizacin es la herramienta para evitar este tipo de problemas, definiendo rdenes estrictos de ejecucin
Hilo A: recuperar c (0) Hilo B: recuperar c (0) Hilo A: incrementar c (1) Hilo B: decrementar c (-1) Hilo A: almacenar c (1) Hilo B: almacenar c (-1)
} c++ 1. 2. 3. c++ 4. 5. 6. est compuesto de: Obtener el valor de c Incrementar c en 1 Almacenar el valor de c est compuesto de: Obtener el valor de c Decrementar c en 1 Almacenar el valor de c
Mtodos sincronizados
Evita que dos invocaciones de mtodos sincronizados del mismo objeto se mezclen. Cuando un hilo ejecuta un mtodo sincronizado de un objeto, todos los hilos que invoquen mtodos sincronizados del objeto se bloquearn hasta que el primer hilo termine con el objeto. Al terminar un mtodo sincronizado, se garantiza que todos los hilos vern los cambios realizados sobre el objeto.
Bloqueo intrnseco
Cuando un hilo invoca un mtodo sincronizado, adquiere el bloqueo intrnseco del objeto correspondiente. Si invoca un mtodo esttico sincronizado, adquiere el bloqueo intrnseco de la clase, independiente de los de sus objetos
Mtodos sincronizados
Hilo 1 Hilo 3 Hilo 2 objeto mtodos ... mtodo sincronizado1
Mtodos sincronizados
class Counter {
private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public int value() { return c; }
Cdigo sincronizado
Cdigo sincronizado
public class MsLunch { private long c1 = 0; private long c2 = 0; private Object lock1 = new Object(); private Object lock2 = new Object(); public void inc1() { synchronized(lock1) { c1++; } } public void inc2() { synchronized(lock2) { c2++; } } }
Espera ocupada: un proceso espera por un recurso, pero la espera consume CPU
while(!recurso)
wait()
Interbloqueo (deadlock): varios procesos compiten por los mismos recursos pero ninguno los consigue Inanicin: un proceso nunca obtiene los recursos que solicita, aunque no est interbloqueado Autobloqueo: un proceso espera por recursos que ya posee sincronizacin reentrante
Sincronizacin reentrante
La sincronizacin reentrante evita que un proceso se bloquee a s mismo en una situacin de autobloqueo
wait() y notify()
wait() suspende el hilo hasta que se recibe una notificacin del objeto sobre el que espera
El proceso que espera debe tener el bloqueo intrnseco del objeto que invoca al wait
Si no, da un error (IllegalMonitorStateException) Una vez invocado, el proceso suspende su ejecucin y libera el bloqueo wait(int time) espera slo durante un tiempo
notify()/notifyAll() informan a uno/todos los procesos esperando por el objeto que lo invoca de que pueden continuar
Sincronizacin
public class SynchronizedPingPong extends Thread { private String word; public SynchronizedPingPong(String s) {word=s;} public void run() Para entrar por aqu tenemos que conseguir el { intrnseco de la clase SynchronizedPingPong synchronized(getClass()) { for (int i=0;i<3000;i++) { System.out.print(word); Ejecuto una iteracin System.out.flush(); getClass().notifyAll(); Aviso de que he terminado try Espero un aviso {getClass().wait();} catch (java.lang.InterruptedException e) {} } getClass().notifyAll(); } } public static void main(String[] args) { SynchronizedPingPong tP=new SynchronizedPingPong("P"); SynchronizedPingPong tp=new SynchronizedPingPong("p"); tp.start(); tP.start(); } }
bloqueo
wait()
this.wait() espera por esta instancia de la clase this.getClass().wait() o getClass().wait() espera por la clase de esta instancia objeto.wait() espera por el objeto que sea. objetoEsttico.wait() espera por la clase del objeto esttico que sea En cualquier caso, dentro de synchronized() sobre el objeto/clase a la que espera Para liberar, notify/notifyAll sobre el objeto/clase sobre la que se espere
Depuracin de interbloqueos
import java.lang.management.*; ... ThreadMXBean tmx = ManagementFactory.getThreadMXBean(); long[] ids = tmx.findDeadlockedThreads(); if (ids != null) { ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true); System.out.println("The following threads are deadlocked:"); for (ThreadInfo ti : infos) System.out.println(ti); }
Carrera 4x100
Tenemos 4 Atletas dispuestos a correr Tenemos una clase principal Carrera Tenemos un objeto esttico testigo Todos los atletas empiezan parados, uno comienza a correr (tarda entre 9 y 11s) y termina su carrera e inmediatamente comienza otro Pistas:
Thread.sleep y Math.random para la carrera synchronized, wait y notify para el paso de testigos System.currentTimeMillis o Calendar para ver tiempos
La carrera amable
Cada atleta llega a meta tras entre 9 y 11s Cuando uno llega a la meta, espera por el siguiente que llegue, y cruza la meta. El nuevo que ha llegado espera por el siguiente, y as sucesivamente Tras cruzarla, imprime su nombre y el tiempo actual
Programar dicho esquema mediante una clase Atleta que implemente Runnable
Pistas:
Thread.sleep y Math.random para la carrera synchronized, wait y notify para la meta System.currentTimeMillis o Calendar para ver tiempos
Sockets en Java
Sockets
Socket = enchufe
Punto final del enlace de comunicacin entre dos procesos (cliente y servidor)
Sockets
asso.usal.es 3) new Socket (asso.usal.es, 3333); Cliente 3333 2) ServerSocket.accept() 4) comunicacin segn protocolo Servidor 1) new ServerSocket(3333);
Socket
public class EchoClient { public static void main(String[] args) throws IOException { Socket echoSocket = null; PrintWriter out = null; BufferedReader in = null; String ip="carpex.fis.usal.es"; try { Abrir echoSocket = new Socket(ip, 80);//nuevo socket conectado a IP y puerto socket out = new PrintWriter(echoSocket.getOutputStream(), true);//salida al socket flujos in = new BufferedReader(new InputStreamReader( //entrada al socket echoSocket.getInputStream())); } catch (UnknownHostException e) { System.err.println("Don't know about host: "+ip); System.exit(1); } catch (IOException e) { System.err.println("Couldn't get I/O for the connection to: "+ip); System.exit(1);} BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); String userInput; while ((userInput = stdIn.readLine()) != null) { out.println(userInput); System.out.println("echo: " + in.readLine()); } stdIn.close(); out.close(); Cerrar flujos y in.close(); echoSocket.close(); socket } }
ServerSocket
1) Establecer el servicio
ServerSocket server = new ServerSocket(3333);
3) Procesar peticiones
in = new BufferedReader(new InputStreamReader(client.getInputStream())); out = new PrintWriter(client.getOutputStream(), true); while(true) { line = in.readLine(); out.println(line); //Servicio de echo }
4) Terminar el servicio
in.close();
out.close();
server.close();
Documentacin y ejercicios
Descargar y ejecutar las clases para implementar el servicio KnockKnock de pgina del tutorial (Writing a Client/Server Pair)
RMI
Remote Method Invocation
RMI
Remote Method Invocation: permite que un objeto corriendo en una JVM use mtodos de otro corriendo en otra JVM (local o remota)
RMI introduccin:
http://download.oracle.com/javase/tutorial/networking/socket s/clientServer.html
Registrados mediante el registro RMI Pasados por referencia en invocaciones remotas Gestionado por el servidor RMI, para el usuario es como llamar a mtodos locales
Implementacin
1. Definir interfaz con los mtodos remotos
Ser conocida por cliente y servidor
2. Implementar la clase que dar el servicio de la interfaz (el servidor) 3. Implementar el cliente que usar el servicio 4. Instanciar el servidor y registrarlo mediante un stub:
Referencia remota al servidor generada por RMI para el uso de los clientes
Interfaz
/** * Interfaz para un servicio RMI de corredores de bolsa * Debe heredar de java.rmi.Remote * Debe manejar RemoteException en sus mtodos */ public interface Corredor extends Remote { String listarTitulos() throws RemoteException; void comprar(String nombre, int cantidad) throws RemoteException; void vender(String nombre, int cantidad) throws RemoteException; }
Slo los mtodos del objeto que se encuentren en una interfaz Remote sern accesibles va RMI La misma interfaz (idealmente ya compilada y en un .jar) debe importarse en cliente y servidor
Servidor y Stub
/** * Servidor de bolsa que implementa Corredor * Debe implementar una interfaz de java.rmi.Remote * */ public class Bolsa implements Corredor { String listarTitulos() throws RemoteException {...} void comprar(String nombre, int cantidad) throws RemoteException; {...} void vender(String nombre, int cantidad) throws RemoteException; {...} //Cualquier otra funcin interna que sea necesaria }
//Registramos un objeto Bolsa de nombre LaBolsa String nombre="LaBolsa"; Corredor motor=new Bolsa(); Corredor stub=(Corredor) UnicastRemoteObject.exportObject(motor,0); Registry registro=LocateRegistry.getRegistry(); registro.rebind(nombre,stub);
Cliente
public class Cliente { public static void main(String args[]) { try { String nombre="LaBolsa"; //Instanciar el registro RMI Registry registro=LocateRegistry.getRegistry(args[0]); //Instanciar un objeto de la clase del servidor Corredor corredor=(Corredor) registro.lookup(nombre); //Uso del servicio ... } catch (Exception e) { System.err.println("Excepcin en el cliente de la bolsa:"); e.printStackTrace(); } } }
Esquema
pepe.usal.es 0) Debe compartirse Con todos los clientes O servicios que la usen 1) El registro rmi debe estar corriendo (rmiregistry)
Registry registro= LocateRegistry.getRegistry("maria.usal.es");
implements Servicio
Cliente
Corredor corredor=(Corredor) registro.lookup(servicio);
System.setProperty("java.rmi.server.codebase", "http://maria.usal.es/rmi/servicio.jar");
3) Como tenemos la interfaz, gracias a la carga dinmica de clases, podemos usar los mtodos implementados
2) La parte del objeto que implementa la interfaz se encuentra en un codebase pblico, p. ej: http://maria.usal.es/rmi/servicio.jar
Servidor y seguridad
Si queremos cargar o enviar clases distintas del API de Java, hay que implementar un gestor de seguridad
Podemos modificar la poltica de seguridad de Java con un fichero de permisos con lneas como:
grant { permission java.security.AllPermission; };
-Djava.security.policy=grantFilePath
Codebase
El codebase es un lugar desde el que se cargan clases en la JVM El CLASSPATH es un codebase local El codebase remoto se usa para acceder a clases desde applets o RMI, a travs de urls Se puede modificar con el argumento de la JVM -Djava.rmi.codebase=url
En RMI, nuestro codebase debe apuntar a las clases compiladas que se comparten
La interfaz o interfaces y cualquier clase que no est en la API y sea argumento de las interfaces
Naming
Naming.lookup() y Naming.rebind() funcionan como sus homnimos de Registry, pero no necesitamos obtener previamente el registro
Errores frecuentes
En el servidor:
No haber iniciado el registro RMI No tener definida el path al codebase de RMI No utilizar la misma clases compiladas para la interfaz (generalmente lo mejor es usar un .jar) Si enviamos clases no definidas en el API:
En servidor y cliente
Procedimiento: interfaz
import java.rmi.*; public interface Corredor extends Remote { String listarTitulos() throws RemoteException; void comprar(String nombre, int cantidad) throws RemoteException; void vender(String nombre, int cantidad) throws RemoteException; }
Bien como las clases tal cual (.java) O (mejor) como las clases compiladas (.class o .jar)
Procedimiento: interfaz
Distribuir el .jar entre los servidores/clientes para que quieran implementar/usar el servicio O distribuir la carpeta con los .class o .java
Procedimiento: servidor
public class Bolsa implements Corredor { public java.util.TreeMap<String, Integer> listado; String listarTitulos() throws RemoteException {...} void comprar(String nombre, int cantidad) throws RemoteException {...} void vender(String nombre, int cantidad) throws RemoteException {...} public static void main(String[] args) { try{ System.setProperty("java.rmi.server.codebase", "file:///PathAClassOJarDeInterfaz"); Corredor motor=new Bolsa(); Corredor stub=(Corredor) UnicastRemoteObject.exportObject(motor,0); Naming.rebind("//servidor:puertoRMI/nombreServicio", stub); System.out.println("La bolsa est en marcha."); } catch (Exception e) {e.printStackTrace();}
}
}
Procedimiento: servidor
Debe importar la interfaz a travs del .jar Debe implementar los mtodos de la interfaz El registro RMI debe estar corriendo antes de hacer llamadas a rebind() Debe hacer referencia al codebase para la interfaz (.jar o carpeta con binarios) a travs de una url a fichero local (file://) o remoto (http://) Si la interfaz implica enviar o recibir como argumentos clases no incluidas API, debe implementar la poltica de seguridad
Procedimiento: cliente
public class Cliente { public static void main(String args[]) { try { Corredor corredor=(Corredor) Naming.lookup("rmi://servidor:puertoRMI/nombreServicio"); System.out.println("Listado de titulos:"); System.out.println(corredor.listarTitulos()); System.out.println("Ahora vendo 10 de Danone."); corredor.vender("Danone",10); } catch (Exception e) {e.printStackTrace();} } }
Procedimiento: cliente
Debe importar el .jar o las clases compiladas de la interfaz Hace referencia a la mquina donde est registrado el objeto remoto y al nombre con el que se ha registrado Si la interfaz implica enviar o recibir como argumentos clases no incluidas API, debe implementar la poltica de seguridad
Ejercicio
El resto seris especuladores que se matan en el parqu a comprar y vender El servicio de Bolsa, da igual cul, debe proveer los servicios del Corredor de bolsa (ver arriba):
listarTitulos: devuelve una lista de los ttulos de esa bolsa y la cantidad de acciones disponibles en venta comprar y vender: de un ttulo, la cantidad especificada
FAQ
Sobre codebase:
http://download.oracle.com/javase/1.4.2/docs/guide/rmi/codebase.html Sobre RMI Sobre
FAQ
rmiregistry [port]
LocateRegistry.createRegistry(int port);
FAQ
Esto ocurre porque el servidor no encuentra los codebase para las clases registradas en RMI Hay que definir la ruta a los codebases para RMI de una de estas maneras: -Djava.rmi.server.codebase=urlClases
System.setProperty("java.rmi.server.codebase", "file:/Users/rodri/Documents/workspace/assoo/bin/");
Ojo: debe ser una url, apuntar a las clases compiladas (/bin/), y terminar en / Ojo: necesitamos el codebase para hacer el stub, as que la llamada debe estar ANTES de ese punto
FAQ
Hemos activado el gestor de seguridad, pero tiene una seguridad muy alta/indadecuada
Crear un fichero file.policy con la poltica de la seguridad y enlazarlo con -Djava.security.policy
FAQ
Con RMI conseguimos llamar a un objeto desde otro ordenador, sin necesidad del cdigo del objeto, pero s de su interfaz. Por tanto, la clase interfaz (bien el cdigo .java, bien compilada importando la carpeta con los .class o en un .jar), debe ser importada por el servidor y el cliente (ambos deben tener copia de esos ficheros). Ojo, en caso de usar el cdigo sin compilar, no funcionar si tenemos interfaces que no son idnticas en servidor y cliente, e incluso puede no funcionar an siendo las mismas (si las versiones de la JVM son distintas, por ejemplo).
FAQ
No. La poltica de seguridad es slo necesaria si los mtodos de nuestra interfaz tienen como argumentos (o devuelven) clases que no estn en el API. Si este es el caso (lo cual no es muy frecuente), tenemos que poner el cdigo para el SecurityManager y establecer en un fichero la poltica de privacidad (ver el .pdf de la presentacin para ms informacin)
FAQ
S, el servidor debe decirnos dnde est el cdigo base del objeto(s) remoto(s), mediante la propiedad java.rmi.server.codebase, que apunta a una url con file://, en cuyo caso los objetos remotos debern estar en la misma mquina que el servicio, o con una url con http://, en cuyo caso podrn estar en mquinas distintas al servidor. El codebase se puede establecer como argumento a la JVM (-Djava.rmi.server.codebase=url) o desde el propio programa (System.setProperty("java.rmi.server.codebase", "url"))
FAQ
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: java.lang.ClassCastException: java.io.ObjectStreamClass cannot be cast to java.lang.String
Es un error bastante puetero, pues no nos da informacin relevante de lo que est pasando. El problema es que tenemos varias rdenes instaladas para ejecutar un servicio de registro RMI. En particular, en los Fedora del laboratorio tenis dos rdenes para rmiregistry:
Y se ejecuta por defecto el de /usr/bin. Tendremos que correr el de la jdk para solventar este problema
FAQ
Es decir, labhp10 seala a 127.0.0.1, as que si el cliente accede via RMI a labhp10, est accediendo realmente a 127.0.0.1, que en el cliente se referir a s mismo y no al servidor. El modo de solucionarlo es editar /etc/hosts:
(para saber vuestra ip en fedora: /sbin/ifconfig -a) Otra opcin (quizs ms inteligente) es publicar el servicio con la ip en vez de con el nombre
FAQ
Se pueden incluir en Java algunas de las tareas de terminal o de las opciones de la VM?
S, por ejemplo:
Puedes, en general, lanzar cualquier orden del sistema con Runtime().getRuntime.exec() Puedes crear el registro RMI con Runtime.getRuntime().exec(rmiregistry) o con LocateRegistry.createRegistry() Puedes conocer tu IP con
InetAddress.getLocalHost().getHostAddress()
Puedes pasar propiedades como por ejemplo java.rmi.server.codebase con System.setProperty(propiedad, valor)