Sei sulla pagina 1di 22

Java World Captulo 7

Captulo 8 Clases internas


Una clase interna bsicamente es lo que el nombre indica, una clase que se encuentra dentro de otra clase. Existen diferentes tipos
de ellas, y las maneras de instanciar las mismas tambin es diferente, de manera que las veremos una a una.
Durante el captulo veremos 4 conceptos:

Clases internas
o Clases internas normales
o Clases internas de mtodo
o Clases internas annimas
Clases estticas anidadas

Algunas reglas sobre las clases internas:

Tienen acceso a todos los atributos y mtodos de su clase contenedora, por ms que los mismos sean private.
Debe de existir una instancia de la clase contenedora para poder crear una instancia de la clase interna, sin excepcin.
Las clases internas siempre mantienen una referencia a la clase contenedora.
Al igual que las clases normales, no es posible definirlas como abstract y final.

Clase interna normal


class Externa {
class Interna {
}
}
Primer punto a tener en cuenta, es que este cdigo al precompilarlo genera dos clases:

Externa.class
Externa$Interna.class

Una clase interna no puede tener mtodos estticos.

Instanciacin
Desde dentro de la clase externa, la clase interna es visible, de manera que lo siguiente es vlido:

class Externa {
public void hazAlgo() {
Interna i = new Interna();
}
class Interna {
}
}
A su vez, la clase interna tiene conocimiento de la clase externa, pudiendo acceder a sus mtodos y atributos como propios.

h
t
t
p
:
/
/
g
u
s
t
a
v
o
a
l
b
e
r
o
l
a
.
b
l
o
g
s
p
o
t
.
c
o
m

h
t
t
p
:
/
/
v
a
l
o
r
c
r
e
a
t
i
v
o
.
b
l
o
g
s
p
o
t
.
c
o
m

Java World Captulo 7

Instanciacin fuera de la clase


Dado que una clase interna puede acceder a los atribtos y mtodos de la clase externa, esto nos dice una cosa: tiene que haber
primero una instancia de la clase externa para que pueda existir una instancia de la clase interna.
Bien, segn el cdigo anterior, la instanciacin de ambas clases desde fuera de la clase sera:
Externa externa = new Externa();
Externa.Interna interna = externa.new Interna();

Referenciando a la clase externa desde la interna


Dado que el modificador this se encuentra reservado para el objeto que llam el mtodo actual en ejecucin, no nos sirve para
invocar otra instancia. Para poder referenciar a instancia de la clase externa dentro de la interna la sintaxis sera:
[NombreClaseExterna].this

class Externa {
class Interna {
public void hazAlgo() {
Externa externa = Externa.this; //Referencia de la clase contenedora
Interna interna = this;
//Referencia de la propia clase interna
}
}
}

Modificadores aplicables
Los modificadores de acceso que se pueden aplicar a la clase interna normal son:

final
abstract
public
private
protected
static (transforma la clase en una clase comn. Lo veremos ms adelante)
strictfp

Clase interna de mtodo local


Es posible declarar una clase interna dentro de un mtodo.
class Externa {
public void hazAlgo() {
class Interna {
public void hazOtraCosa() {}
}
}
}

El que hayas declarado una clase de mtodo local no significa que hayas creado una instancia de esta clase.

De manera que si quisieramos crear una instancia de Interna, esto solo podra realizarse dentro del mtodo hazAlgo().

Java World Captulo 7

Cosas que se pueden y no hacer dentro de una clase interna de mtodo

No se puede acceder a las variables definidas dentro del mtodo, salvo que estas se definan como final.
Puede acceder a los atributos de la clase externa.
o Solo podr acceder a atributos estticos, si es que fue declarada como static (no existe el this).
La clase puede declararse dentro del mtodo, pero la referencia puede almacenarse en otra referencia, de manrea que
finalizado el mtodo el objeto siga existiendo.

Modificadores aplicables
Los modificadores de acceso que se pueden aplicar a una clase interna de mtodo local son:

abstract
final

Clases internas annimas


Estas conforman las porciones de cdigo ms extraas que podamos encontrar en Java. Veamos un ejemplo primero:
class ClaseDos {
public void hazAlgo() {}
}
class Original {
ClaseDos c = new ClaseDos() {
public void hazAlgo() {
System.out.println("Mtodo pop sobrescrito");
}
};
}

La declaracin de una clase annima debe cerrar la ltima llave con un ; (punto y coma), salvo que se este
definiendo como parmetro de un mtodo.

El concepto de annima viene porque se est sobreescribiendo una clase (o interfaz), pero no se est definiendo un nuevo tipo.
Conceptos a tener en cuenta de la clase annima:

La clase annima puede extender de una clase normal, abstract o implementar una interface, pero solo puede
extender/implementar de una de las anteriores mencionadas (no puede extender de a e implementar la interfaz b).
Desde fuera de la clase annima solo se pueden invocar los mtodos definidos en la super clase o interfaz.
Se pueden crear nuevos mtodos, pero estos solo pueden ser invocados desde otro mtodo de la clase annima.
Tambin es posible crear una clase annima como parmetro de una funcin.

h
t
t
p
:
/
/
g
u
s
t
a
v
o
a
l
b
e
r
o
l
a
.
b
l
o
g
s
p
o
t
.
c
o
m

h
t
t
p
:
/
/
v
a
l
o
r
c
r
e
a
t
i
v
o
.
b
l
o
g
s
p
o
t
.
c
o
m

Java World Captulo 7


Veamos un ejemplo de una clase annima declarada como parmetro de un mtodo, que a su vez extiende de una clase abstract.
class Perro {
public void perseguir(Gato g) {
System.out.println("El perro comienza a perseguir al gato");
g.huir();
}
}
abstract class Gato {
abstract public void huir();
}
class Duenio {
public void hazAlgo() {
Perro p = new Perro();
p.persegir(new Gato() {
public void huir() {
System.out.println("El gato logr huir exitosamente");
}
});
}
}

El perro comienza a perseguir al gato


El gato logr huir exitosamente

Crear una clase annima a partir de una interfaz


Una clase annima puede implementar una interfaz, pero la declaracin de la misma no es como se hace normalmente. Veamos un
ejemplo:
interface Animal {
public void emitirSonido();
}
class Familia {
Animal mascota = new Animal() {
public void emitirSonido() {
System.out.println("wof wof!");
}
};
mascota.emitirSonido();
}

wof wof!

Como podemos apreciar, se utiliza tanto clases como interfaces de la misma manera en cuanto a clases annimas se refiere.
new [Class o Interface]() {}

Java World Captulo 7

Clases estticas anidadas


Una clase esttica anidada es simplemente una clase definida como un atributo esttico de la clase padre.
Conceptos que cambian con respecto a las clases internas:

No requieren de una clase externa para poder crear una instancia de la interna.
No pueden acceder a atributos y mtodos de la clase externa que no estn definidos como static.
Se comporta como una clase normal, solo que la ruta de acceso es a travs de ClaseExterna.ClaseInterna.
o Salvo en el caso que estemos en un mtodo de la clase externa, en el cual la clase interna puede ser accedida
directamente como ClaseInterna.

class Persona {
static class Personita {
public void hazAlgo() {
System.out.println("Personita hace algo");
}
}
}
public class General {
static class Cabo {
public void hazAlgo() {
System.out.println("Cabo hace algo");
}
}
// Tener en cuenta que el mtodo main est definido dentro de General
static public void main(String[] args) {
Persona.Personita p = new Persona.Personita();
Cabo c = new Cabo();
p.hazAlgo(); // Personita hace algo
c.hazAlgo(); // Cabo hace algo
}
}

Captulo 9 Hilos de ejecucin (Threads)


Un hilo de ejecucin es un proceso liviano desencadenado por otro proceso, y dependiente de este ltimo. El nuevo hilo tendr un
stack aparte.
En palabras ms simples, un hilo de ejecucin es un proceso paralelo con respecto al proceso principal, lo que lleva a cabo que en
computadoras con ms de un nucleo (o emulados), haya varios procesos ejecutando nuestro cdigo al mismo tiempo.
Que representa para Java un Thread?

Una clase java.lang.Thread


Un hilo de ejecucin

Para el examen es necesario conocer cuando una sintaxis est creando cdigo multithread seguro y cuando no.

h
t
t
p
:
/
/
g
u
s
t
a
v
o
a
l
b
e
r
o
l
a
.
b
l
o
g
s
p
o
t
.
c
o
m

h
t
t
p
:
/
/
v
a
l
o
r
c
r
e
a
t
i
v
o
.
b
l
o
g
s
p
o
t
.
c
o
m

Java World Captulo 7

Creando un thread
Dentro de la clase java.lang.Thread se encuentran una serie de mtodos. Dentro de estos debers de conocer los siguientes:

start()
yield()
sleep()
run()

Para poder crear un nuevo Thread debes de cumplir el contrato de la interfaz java.lang.Runnable, el cual tiene un solo mtodo:
public void run();
Para ello, la clase que utilicemos tiene que implementar dicha interfaz, o extender la clase java.lang.Thread.

Definiendo un Thread extendiendo Thread


class MiClaseThread extends Thread {
public void run() {
System.out.println("Oh, JavaWorld esta en un Thread!");
}
}

Definiendo un Thread implementando Runnable


class MiClaseRunnable implements Runnable {
public void run() {
System.out.println("Oh, JavaWorld esta en un Thread!");
}
}

Instanciando un Thread
Si o si es necesaria una instancia de Thread para poder correr un hilo de ejecucin.
Para eso tenemos dos posibilidades, dependiendo de si extendimos de Thread, o implementamos Runnable.

Instanciando un Thread que extiende de Thread


Thread hilo = new miClaseConThread();
Creamos un objeto MiClaseConThread y lo asignamos a una referencia de tipo Thread, dado que extiende de Thread, la relacin Is-A
Thread es legal.

Instanciando un Thread que implementa Runnable


Runnable hiloRunnable = new miClaseConThread();
Thread hilo = new Thread(hiloRunnable);
// O la versin abreviada
Thread hilo = new Thread(new miClaseConThread());
En este caso, lo que hacemos es primero crear una instancia de nuestra clase y almacenarla en una referencia de tipo Runnable.
Luego, utilizamos uno de los constructores sobrecargados de Thread, el cual recibe un objeto de tipo Runnable.

Java World Captulo 7

La clase Thread
Como mencionamos anteriormente, Thread tiene una serie de constructores sobrecargados:

Thread()
Thread(Runnable objetivo)
Thread(Runnable objetivo, String nombre)
Thread(String nombre)

Si probamos los cdigos anteriores en una aplicacin en Java, notaramos que no pasa nada. La cuestin es que el instanciar un
Thread no hace que se lance el hilo de ejecucin del mismo, de manera que no se ejecuta. Para poder determinar en que
condiciones de ejecucin se encuentra el Thread lo podemos clasificar en tres estados:
Estado

Descripcin

new
(nuevo)
alive
(vivo)

Cuando un Thread ha sido instanciado (invocacin del new) se encuentra en estado nuevo.

dead
(muerto)

Cuando el hilo de ejecucin completa el mtodo run, se dice que el Thread est muerto.

Cuando el hilo de ejecucin es lanzado (se crea un nuevo stack para el hilo y se ejecuta el contenido de run() ), se dice
que el Thread est vivo.

Iniciando la ejecucin de un Thread


Simplemente, para iniciar la ejecucin de un nuevo Thread, se utiliza el mtodo start().
Runnable hiloRunnable = new miClaseConThread();
Thread hilo = new Thread(hiloRunnable);
hilo.start();
Tambien hay que tener mucho cuidado con la llamada a run(). Cuando invocamos el mtodo start(), este primero crea un nuevo
stack, y luego lanza el mtodo run() como miembro inicial de dicho stack en un hilo de ejecucin aparte.

Si se invoca directamente el mtodo run() no generar un error, pero el mismo no se ejecutar en un nuevo
thread, sino que sera como llamar a un mtodo normal.

h
t
t
p
:
/
/
g
u
s
t
a
v
o
a
l
b
e
r
o
l
a
.
b
l
o
g
s
p
o
t
.
c
o
m

h
t
t
p
:
/
/
v
a
l
o
r
c
r
e
a
t
i
v
o
.
b
l
o
g
s
p
o
t
.
c
o
m

Java World Captulo 7

Ejecutando Threads
Vamos a lo que nos concierne, corramos algunos hilos:
class PruebasRunnable implements Runnable {
private int iteraciones = 4;
public void run() {
for (int x = 0 ; x < iteraciones; x++ ) {
System.out.println("Ejecutando [" + Thread.currentThread().getName() + "] [" + x
+ "]");
}
}
static public void main(String[] args) {
Runnable hilo = new PruebasRunnable();
Thread hilo1 = new Thread(hilo, "Hilo 1");
Thread hilo2 = new Thread(hilo, "Hilo 2");
Thread hilo3 = new Thread(hilo, "Hilo 3");
hilo1.start();
hilo2.start();
hilo3.start();
}
}
Ejecutando
Ejecutando
Ejecutando
Ejecutando
Ejecutando
Ejecutando
Ejecutando
Ejecutando
Ejecutando
Ejecutando
Ejecutando
Ejecutando

[Hilo
[Hilo
[Hilo
[Hilo
[Hilo
[Hilo
[Hilo
[Hilo
[Hilo
[Hilo
[Hilo
[Hilo

1]
2]
3]
2]
1]
2]
3]
2]
1]
3]
1]
3]

[0]
[0]
[0]
[1]
[1]
[2]
[1]
[3]
[2]
[2]
[3]
[3]

El orden de ejecucin de los hilos no puede ser jams predecido.

Por ms que conozcamos el orden en que son inicializados, ni siquiera esto nos asegura de que el orden de ejecucin de los mismos
sea ese.
El sistema de hilos y tareas es manejado por el Sistema Operativo en conjunto con la JVM, son ellos quienes asignarn el proceso y
otorgaran los tiempos que el mismo se encuentra en ejecucin.
Otra cuestin a tener en cuenta es que un Thread solo puede ser llamado una nica vez en toda su existencia.

El llamar al mtodo start() ms de una vez desde el mismo Thread generar el lanzamiento de una
excepcin IllegalThreadStateException.

Java World Captulo 7

Estados y transiciones de los threads


En total podemos contar 5 estados en los cuales puede estar un thread. Veamos un grfico de esto para entender un poco mejor:
Alive (vivo)
Waiting
/Blocking
(esperando
/bloqueado)

New
(nuevo)

Runnable
(esperando
ejecucin)

Tabla de estados
Estado

Dead
(muerto)

Running
(en
ejecucin)

Descripcin

New
(nuevo)
Runnable
(esperando ejecucin)
Running
(en ejecucin)
Waiting/Block
(esperando/bloqueado)

Dead
(muerto)

Este estado se da cuando se ha invocado al new del Thread, pero aun no se ha llamado al mtodo
start().
Este estado se da cuando el thread se encuentra listo para ser ejecutado, y est esperando que el
gestionador de eventos de la JVM lo seleccione para comenzar la ejecucin.
Este estado se da cuando el thread fue seleccionado por el gestionador de eventos y se encuentra
actualmente en ejecucin.
Este estado se da cuando el thread requiere algn recurso (I/O por ejemplo), de manera que para
no consumir recursos de CPU queda esperando. Tambin puede pasar que se le haya ordenado que
se durmiera, de manera que despertar cuando acabe el tiempo (si se especific), o que otro thread
le enve una seal para despertarlo.
Este estado se da cuando el thread complet la ejecucin del mtodo run. Aun sigue siendo un
objeto, el cual solicito que se liberarn sus recursos.

Durmiendo (Sleep)
Sleep es un mtodo esttico de la clase Thread. Este solo puede afectar al Thread que actualmente se est ejecutando, de manera
que un Thread no puede dormir a otro.
Cuando un Thread es enviado a dormir pasa al estado de Esperando/Bloqueado.
Los mtodos sleep son:

sleep()
sleep(long milisegundos)

El primero enva el thread a Esperando hasta que otro thread lo despierte mediante nofity/notifyAll.
El segundo lo duerme por un tiempo determinado.
Cuando se especifica un tiempo de sleep, este representa el tiempo mnimo que el thread permanecer
dormido, pero no es el tiempo exacto.

10

h
t
t
p
:
/
/
g
u
s
t
a
v
o
a
l
b
e
r
o
l
a
.
b
l
o
g
s
p
o
t
.
c
o
m

11

h
t
t
p
:
/
/
v
a
l
o
r
c
r
e
a
t
i
v
o
.
b
l
o
g
s
p
o
t
.
c
o
m

Java World Captulo 7


Adems, este mtodo puede generar una excepcin InterruptedException, por lo que hay que llamarlo dentro de un Try-Catch.
Hagamos un ejemplo simple:

import java.lang.InterruptedException;
public class PruebasRunnable01 implements Runnable {
static public void main(String[] args) {
(new Thread(new PruebasRunnable01())).start();
}
public void run() {
for (int x = 1 ; x <= 101 ; x ++) {
if (x%10 == 0) {
System.out.println("Running " + x);
}
try {
Thread.sleep(500); // 1 milisegundo * 500 = 1/2 segundo
} catch(InterruptedException ex) {}
}
}
}

Yield() y threads con prioridades


Los threads siempre se ejecutan con una prioridad, generalmente, esta se representa con un nmero del 1 al 10.
Algunas JVMs utilizan una lgica de tiempo cclico, la cual asigna un tiempo determinado a un thread para que se ejecute. Si el
thread excede el tiempo, se pausa su ejecucin, y se devuelve a la lista de esperando ejecucin, dando la oportunidad a otro
thread para que se ejecute. Otras utilizan una lgica que permiten a un thread ejecutarse hasta la finalizacin del mtodo run.
Las prioridades funcionan de manera que se ordena la lista de esperando ejecucin de menor a mayor por prioridad, siempre
pasando a en ejecucin a los threads de mayor prioridad.

La JVM no especifica que las prioridades se sigan al pie de la letra, de manera que solo utilizamos estas para
optimizar el cdigo, pero no dependan de ellas para determinar la lgica del programa.

Esto nos deja con dos posibles comportamientos:

Se obtiene un thread de la lista de esperando ejecucin y se lo ejecuta hasta que se complete o se bloque.
Se utilizan tiempos mximos de ejecucin, de manera que todos los threads (o la gran mayora), tengan una oportunidad de
ejecutarse.

Estableciendo la prioridad de los threads


El thread recibe una prioridad por defecto, la cual corresponde a la prioridad del thread con que fue creado. Esta comprende un
rango que va generalmente del 1 al 10, pero no todas las JVMs contienen este rango.
Por defecto, los threads main son creados con prioridad 5.
Existen tambin algunos identificadores en la clase Thread para indicar las prioridades:

Thread.MIN_PRIORITY
Thread.NORM_PRIORITY
Thread.MAX_PRIORITY

1
5
10

La prioridad se establece mediante el mtodo setPriority(int prioridad). Este es un mtodo de instancia perteneciente a la clase
Thread.

Java World Captulo 7


Ejemplo:
Thread t = new MiThread();
t.setPriority(Thread.MAX_PRIORITY);
t.start();
La JVM nunca cambiar la prioridad de un thread.

El mtodo yield()
Este mtodo lo que hace es enviar una sugerencia a la JVM para que el thread actual pase a la lista de esperando ejecucin. Pero
hay que tener en cuenta que esto es una sugerencia, y puede que nunca se cumpla. Tambin puede que se cumpla, pero al enviar el
thread a la lista de esperando ejecucin, vuelva a ser elegido el mismo y pase a la lista de en ejecucin.
Mediante el mtodo yield(), el thread nunca pasar al estado de Esperando. Este es un mtodo de clase perteneciente a la clase
Thread.

El mtodo join()
Este mtodo permite unir la ejecucin de un thread al final de la otra. Supongamos que tenemos dos threads A y B. A requiere que B
se ejecute para poder continuar, de manera que A realiza un join contra B. Cuando B termine su ejecucin, recin en ese momento A
ser candidato a ser elegido de la lista de esperando ejecucin, pero no antes.
Este es un mtodo de instancia perteneciente a la clase Thread.

Sincronizacin de cdigo
La sincronizacin es el termino utilizado para que varios elementos que utilizan un recurso compartido no generen inconsistencia de
datos.
En el siguiente ejemplo de cdigo pueden ver un caso en particular con una cuenta bancaria. Ejecutenlo varias veces y vern como a
veces el saldo queda en negativo y otras no.
Para ver el ejemplo, dirgete al enlace y compila el cdigo.
Lo que debemos hacer en orden de preservar la integridad de la cuenta es que el mtodo efectuarRetiro se realice de manera
atmica (en realidad el trmino no es del todo correcto, dado que una operacin atmica es aquella que solo contiene una
instruccin. En este caso, se utiliza para identificar un conjunto de instrucciones que deben de procesarse como una sola).
Para ello podemos utilizar el modificador synchronized. Este lo que hace es asegurarse de que solo un objeto por vez pueda
acceder al recurso (en este caso el mtodo), pero no asegura que el objeto ejecute en una pasada todo el cdigo.
Si un objeto ejecuta el cdigo y se interrumpe dentro del mismo, se mantiene una llave, de manera que otro objeto no pueda
acceder hasta que el que estaba bloqueado, vuelva y termine con la ejecucin.
Veamos el mismo ejemplo pero ahora sincronizado. Ejecutenlo las veces que quieran. Vern que el saldo de la cuenta jams ser
negativo.
Para ver el ejemplo, dirgete al enlace y compila el cdigo.

12

h
t
t
p
:
/
/
g
u
s
t
a
v
o
a
l
b
e
r
o
l
a
.
b
l
o
g
s
p
o
t
.
c
o
m

13

h
t
t
p
:
/
/
v
a
l
o
r
c
r
e
a
t
i
v
o
.
b
l
o
g
s
p
o
t
.
c
o
m

Java World Captulo 7

Sincronizacin y bloqueos
La sincronizacin funciona mediante bloqueos. Denominamos bloqueo al mtodo que implementa synchronized, y monitor al
objeto que accede al bloqueo.
Puntos clave sobre synchronized:

Solo se pueden sincronizar mtodos o bloques de cdigo.


Cada objeto solo tiene un bloqueo.
No es necesario sincronizar todos los mtodos de una clase.
Solo puede acceder un monitor al bloqueo por vez.
Si se utiliza la sincronizacin en forma incorrecta, se pueden llegar a generar deadlocks (bloqueos de la muerte). Esto
sucede cuando dos objetos, contienen un bloqueo, y ambos requieren acceder al bloqueo del contrario.
Si un thread pasa a esperando/bloqueado, este mantiene todos los bloqueos que tiene (continua siendo monitor).
Un thread puede adquirir ms de un bloqueo.
Es posible sincronizar un bloque de cdigo en vez de un mtodo completo.

//Mtodo de instancia sincronizado


public synchronized hazAlgo() {
System.out.println("Codigo sincronizado");
}
//Bloque de cdigo de instancia sincronizado
public void hazAlgo() {
synchronized(this) {
System.out.println("Codigo sincronizado");
}
}
//Mtodo de clase sincronizado
static public synchronized hazAlgo() {
System.out.println("Static - Codigo sincronizado");
}
//Bloque de cdigo de clase sincronizado
static public hazAlgo() {
synchronized(MyClase.class) {
System.out.println("Static - Codigo sincronizado");
}
}

Java World Captulo 7

Que pasa si un thread no obtiene el bloqueo?


Si un objeto intenta acceder a por ejemplo un mtodo sincronizado, y este ya est bloqueado, el objeto en cuestin queda en una
lista de espera especial para cuando el bloqueo sea liberado.
No hay una lgica que indique en que orden se obtiene el prximo objeto que se encuentra en la lista esperando el bloqueo.
Cuadro de cmo afectan los mtodos de threads a los bloqueos
Libera el bloqueo
Mantiene el bloqueo
Clase que contiene el mtodo
wait()

notify()
join()
sleep()
yield()

java.lang.Object
java.lang.Thread
java.lang.Thread
java.lang.Thread

Cuando es necesario sincronizar?


Generalmente, siempre que utilicemos threads ser necesario que hagamos uso de la sincronizacin, siempre que tanto en mtodos
estticos como no estticos, haya datos que puedan sufrir modificaciones y que no sean de tipo local, se debern de marcar como
synchronized.
Hay un pequeo tema, dijimos que cada clase tiene un bloqueo disponible, pero es uno para la clase y uno para la instancia (static
y no static). El problema es que si un mtodo de clase llama a un atributo esttico, y el primero est sincronizado, solo se toma un
bloqueo, el de la clase. Y si otro objeto llama al mtodo esttico, y este llama al atributo de clase, solo se toma el bloqueo esttico. Y
con esto ambos objetos pudieron saltear los bloqueos del otro.
De ms est decir que esta situacin debes evitarla siempre que sea posible de manera que:

Los atributos estticos solo se deben invocar desde mtodos estticos sincronizados.
Los atributos de clase solo se deben invocar desde mtodos de clase sincronizados.

Clases multi-hilo seguro


Cuando una clase contiene mtodos synchronized cuidadosamente colocados, se denomina que la clase es multi-hilo seguro
(Thread-Safe). Pero hay un problema, que la clase sea thead-safe no quita que podamos implementarla de manera incorrecta,
produciendo posibles corrupciones de datos.
De manera que cuando utilicemos threads, debemos depender de la clase que llevara a cabo la operacin atmica.

Bloqueos de la muerte
Esto se produce cuando dos bloqueos esperan que se libere el bloqueo del contrario para poder continuar. Esto generalmente es un
error muy difcil de detectar, adems de que cuando trabajamos con threads, el reproducir el error es aun ms complicado.

Interaccin entre threads


Para comunicarse entre si, se utilizan los mtodo: wait(), notify() y notifyAll().

wait(), notify(), notifyAll() solo pueden ser invocados desde un contexto sincronizado. Un objeto no puede
ionvocar dichos mtodos a menos que posea el bloqueo.

Los mtodos wait() y notify() pertenecen a la clase Object.


Un objeto puede tener una lista con 0 .. * (0 a muchos) objetos esperando que se les notifique cuando la ejecucin de este haya
terminado, o el bloqueo se haya liberado. Veamos un ejemplo:

14

h
t
t
p
:
/
/
g
u
s
t
a
v
o
a
l
b
e
r
o
l
a
.
b
l
o
g
s
p
o
t
.
c
o
m

15

h
t
t
p
:
/
/
v
a
l
o
r
c
r
e
a
t
i
v
o
.
b
l
o
g
s
p
o
t
.
c
o
m

Java World Captulo 7

//Compilacin:
//Ejecucin:

javac -g PruebasRunnable04.java
java PruebasRunnable04

public class PruebasRunnable04 {


static public void main(String[] args) {
(new PruebasRunnable04(new Object())).pruebaWait();
}
private Object bloqueador;
public PruebasRunnable04(Object bloqueador) {
this.bloqueador = bloqueador;
}
public void pruebaWait() {
Incrementador incrementador = new Incrementador(bloqueador);
(new Thread(incrementador)).start();
synchronized(bloqueador) {
try {
System.out.println("Esperando a que se complete el incrementador...");
bloqueador.wait();
} catch(InterruptedException ex) {}
System.out.println("El total es: " + incrementador.getTotal());
}
}
}
class Incrementador implements Runnable {
private int contador = 0;
private Object bloqueador;
public Incrementador(Object bloqueador) {
this.bloqueador = bloqueador;
}
public int getTotal() {
return contador;
}
public void run() {
synchronized(bloqueador) {
for(int x = 0 ; x < 100 ; x++) {
contador++;
System.out.print(".");
try {
Thread.sleep(100);
} catch(InterruptedException ex){}
}
System.out.println();
bloqueador.notify();
}
}
}
En el cdigo anterior, no se ejecuta la sentencia que muestra el total hasta que Incrementador no emite el notify indicando que
puede continuar.
La importancia del cdigo anterior radica en que para poder llamar al mtodo wait del objeto bloqueador, se debe de poseer el
bloqueo del mismo.

Java World Captulo 7


Al igual que sleep(), wait() contiene una sobrecarga: wait(long milisegundos). Esto tiene como propsito poner un lmite de tiempo
en la espera, ejecutando lo que ocurra primero (ya sea notify() o el tiempo ha transcurrido).

Cuando se ejecuta un notify() no quiere decir que se libere el bloqueo. Esto se realizar una vez que se salga
del cdigo sincronizado.

Utilizando notifyAll() cuando varios threads puedan estar esperando


Si ms de un thread se encontraba esperando que se le notifique, al ejecutar notifyAll() todos aquellos que se encuentren en dichas
condiciones competirn por parar al estado en ejecucin.

Utilizando wait() en un bucle


Uno de los casos que podemos encontrar y que no hemos planteado aun es el hecho de que el notify() se ejecute antes que el wait().
Qu pasara en esta situacin?...
Simple, si un objeto estuviera esperando un notify() que no volver a ejecutarse (esto es as por nuestra lgica del programa), el
thread estar esperando eternamente, o hasta que se genere una interrupcin (InterruptedException).

Captulo 10 - Deployment
Todo desarrollo en Java requiere que los archivos sean pre-compilados a bytecodes, los cuales son almacenados en archivos .class, o
porque no comprimir todos tus .class en un paquete .jar.
Un recordatorio que mencionamos en el primer captulo. Para poder compilar los archivos .java debes tener instalada alguna
versin de la JDK (Java Development Kit), no basta con tener la JRE (Java Runtime Environment) instalada.

Compilando con con javac


Javac es el comando utilizado para poder transformar nuestros .java a .class (bytecodes). La sintaxis de este es:
javac [opciones] [archivos de cdigo fuente]
Los elementos entre [] significan opcional.
Mltiples opciones y/o archivos de cdigo fuente deben separarse con caracteres de espacio.

Opcion d
Por defecto, cuando compilemos nuestros archivos en Java, el directorio destino por defecto ser exactamente el mismo que el de
los cdigos fuente. Para poder modificar esto existe el comando d.

El directorio que especifiques mediante d debe existir, de lo contrario generars un error de compilacin.

16

h
t
t
p
:
/
/
g
u
s
t
a
v
o
a
l
b
e
r
o
l
a
.
b
l
o
g
s
p
o
t
.
c
o
m

17

h
t
t
p
:
/
/
v
a
l
o
r
c
r
e
a
t
i
v
o
.
b
l
o
g
s
p
o
t
.
c
o
m

Java World Captulo 7


Supongamos la siguiente estructura.
Directorio Raiz

source
com
blogspot
gustavoalberola
PruebaJavaWorld.java
classes
Dentro de source van nuestro .java y dentro de classes nuestros .class.
Si quisiramos compilar el cdigo anterior (tengan en cuenta que segn la estructura anterior, PruebaJavaWorld.java esta dentro de
los package com, blogspot y gustavoalberola) deberamos utilizar la sintaxis:
Parados sobre el directorio source
javac d ../classes com/blogspot/gustavoalberola/PruebaJavaWorld.java
Esto lo que har es crear dentro de classes la misma jerarqua de directorios, con el .class compilado a bytecode.

Ejecutando aplicaciones con java


Una vez que tenemos nuestros .class lo que necsitamos es ejecutar el programa, para ello utilizamos el comando java. Su sintaxis es:
java [opciones] clase [argumentos]
Por defecto, el comando java interpreta que lo que quieres ejecutar es un .class, de manera que no es necesario especificar la
extensin (es ms, si la especificas, no podrs ejecutar tu programa).

Utilizandos las propiedades de sistema


Dentro de java tenemos una clase denominada java.util.Properties. Esta es utilizada para obtener las propiedades por defecto del
sistema operativo, el compilador de java, la jvm, y tambin para guardar nuestras propias propiedades.
Veamos un ejemplo simple:
import java.util.Properties;
public class Pruebas_01 {
static public void main(String[] args) {
Properties propiedades = System.getProperties();
propiedades.setProperty("MiPropiedad", "MiValor");
propiedades.list(System.out);
}
}

Java World Captulo 7


Ejecutando el programa con:
java -DMyOtraPripiedad="Aun no decido que valor poner" Pruebas_01
El resultado es algo asi:
java.runtime.name=Java(TM) SE Runtime Environment
sun.boot.library.path=C:\Program Files\Java\jre6\bin
java.vm.version=14.3-b01
java.vm.vendor=Sun Microsystems Inc.
java.vendor.url=http://java.sun.com/
...
java.runtime.version=1.6.0_17-b04
...
java.specification.version=1.6
user.name=gustavo
java.class.path=.
MyOtraPripiedad=Aun no decido que valor poner
...
MiPropiedad=MiValor

Vemos como tanto la variable que declaramos en cdigo, como aquella que introducimos mediante D existen en el listado de
propiedades.

Manejando argumentos de la lnea de comandos


Todos losargumentos que se escriban en el momento de ejecucin entran en el array de Strings del mtodo main, empezando por el
ndice 0 para el primer argumento, y as sucesivamente.
public class Pruebas_02 {
static public void main(String[] args) {
int x = 0;
for(String s : args) {
System.out.println("Argumento [" + x++ + "] = " + s);
}
}
}
Ejecutando el comando
java Pruebas_02 soy1 "Y yo que?" 1234568
Argumento [0] = soy1
Argumento [1] = Y yo que?
Argumento [2] = 1234568

18

h
t
t
p
:
/
/
g
u
s
t
a
v
o
a
l
b
e
r
o
l
a
.
b
l
o
g
s
p
o
t
.
c
o
m

19

h
t
t
p
:
/
/
v
a
l
o
r
c
r
e
a
t
i
v
o
.
b
l
o
g
s
p
o
t
.
c
o
m

Java World Captulo 7

Buscando otras clases


Cuando compilamos o ejecutamos una aplicacin, esta requiere de otras clases para funcionar. Supongamos que estamos en la
seccin de I/O, y necesitamos la clase File, esta tendra que buscarse en java.io.File. Para ello, java realiza las siguientes acciones:

Tanto java como javac buscan otras clases de la misma manera


Se procede a buscar una a una las clases necesarias
o Se busca dentro de las clases que vienen estandar con J2SE
o Se busca dentro de los classpath declarados
La bsqueda se detiene en la primer coincidencia.
El classpath puede ser declarado de dos maneras:
o Como variable del sistema operativo
o Como argumento en la lnea de comandos (si se especifica, sobrescribe al classpath de sistema, solo en dicha
invocacin).

Declarando y utilizando classpaths


El classpath es una direccin a nivel de directorio en donde se buscaran las clases necesarias para compilar/ejecutar el programa.
El classpath a su vez puede estar conformado por varios classpath concatenados mediante un caracter parser que hace de
separador.
Hay que tener en cuenta que una estructura de directorio se especifica diferente segn el S.O. que estemos utilizando:

Unix: se utiliza como directorio el / y como separador el :


Windows: se utiliza como directorio el \ y como separador el ;.

Un ejemplo de classpath vlido para windows sera:


.\classes;c:\misProyectos\classes;c:\;
Muchas veces cuando ejecutamos un programa podemos toparnos con una excepcin ClassNotFoundException. Esto indica que
la JVM no pudo localizar alguna clase que nuestro programa requiere.
Los classpaths se recorren de izquierda a derecha.
Cuando se utiliza el comando java, este no busca en el directorio donde esta parado las clases, solo en el
directorio de la JRE. Para que haga lo primero, es necesario especificarlo mediante la opcin classpath o
cp.

Package y bsqueda
Existen dos maneras de referenciar clases desde nuestro cdigo que se encuentran en otro package, mediante imports, o la
llamda con el nombre atmico de la clase (el package.classNombre).
package com.blogspot.gustavoalberola;
public class JavaWorld {}

Java World Captulo 7


Dicha clase ser almacenada en com/blogspot/gustavoalberola/JavaWorld.java.
Para referenciarlo desde otro package podemos utiliza cualquiera de las dos sentencias a continuacin:

Modo de referencia atmico

//otro package
public class OtraClase {
static public void main(String[] args) {
com.blogspot.gustavoalberola.JavaWorld javaWorld;
javaWorld = new com.blogspot.gustavoalberola.JavaWorld();
}
}

Modo de referencia mediante imports


//otro package
//Tambien podemos utilizar el * en vez de JavaWorld (importa todas las clases del
package)
import com.blogspot.gustavoalberola.JavaWorld;
public class OtraClase {
static public void main(String[] args) {
JavaWorld javaWorld;
javaWorld = new JavaWorld();
}
}

Paths relativos y absolutos


La diferencia entre un path relativo y absoluto es:

El path relativo depende de donde se encuentra el directorio actual. Ejemplo: ../ vuelve un directorio hacia arriba.
El path absoluto siempre referencia al mismo lugar sin importar donde se encuentre el directorio actual. Ejemplo:
c:\Jre_6_0_17\.

Archivos JAR
Teniendo nuestra aplicacin terminada, es probable que quieras comenzar a distribuirla. Puedes tener todos los .class en sus
respectivos directorios y distribuirlos solo de esa manera, o puedes generar unn archivo jar que contenga todos los .class.
Algunas reglas con respecto a los .jar

Automticamente genera un directorio META-INF.


Automticamente genera un archivo MANIFEST.MF dentro del directorio anterior.
Jams introducir ninguna de tus clases dentro del directorio META-INF.
Se copia la estructura del directorio y subdirectorios tal cual estaba en el file system.
Los comandos java y javac utilizan el .jar con una estructura de directorio normal.

Cuando queremos compilar o ejecutar una clase que requiere de otra que se encuentra en un .jar, es
necesario especificar en el classpath el directorio junto con el nombre y extensin del .jar.
Ejemplo: java -cp=libs/JavaWorld.jar HolaJavaWorld

20

h
t
t
p
:
/
/
g
u
s
t
a
v
o
a
l
b
e
r
o
l
a
.
b
l
o
g
s
p
o
t
.
c
o
m

21

h
t
t
p
:
/
/
v
a
l
o
r
c
r
e
a
t
i
v
o
.
b
l
o
g
s
p
o
t
.
c
o
m

Java World Captulo 7

Utilizando /jre/lib/ext
Cuando instanalamos la JRE, dentro de uno de sus directorios
encontramos jre/lib/ext. Dentro se encuentran todas las clases que java
utiliza. Si pusiramos cualquiera de nuestras clases aqu dentro, no
necesitaramos especificar un classpath para referenciarlas.
Esto es una prctica poco comn, y solo se aconseja para casos personales,
pero no como mtodo de distribucin de un programa.
Tambin es importante saber que es muy comn encontrar variables
definidas para abreviar un directorio, como por ejemplo JAVA_HOME. Si
ves en el examen esto, interpretalo como el directorio de las libreras de
java.

Utilizando import estticos

Fin del camino

El import esttico se utiliza para no tener que escribir el nombre completo


de la constante, mtodo o atributo esttico. Ejemplo:

Con esta ltima entrega concluimos la


saga de Java World SCJP. Ha sido un
camino largo y les agradecemos a todos
aquellos que nos estuvieron siguiendo
todo este tiempo, a aquellos que
realizaron comentarios y correcciones
sobre los captulos previos, y a la
comunidad en general.

import static System.out;


public class HolaMundo {
static public void main(String[] args) {
out.println("Hola mundo");
}
}
Muchos aseguran que esto ahorra un par de tecleados a cambio de perder
en la redibilidad del cdigo.

La llamada debe ser import static, y solo en


ese orden.

Nos quedan un par de cositas para subir


adems de la compilacin final de todos
los captulos para que les sea ms fcil
descargarlos.
Esperamos que el material les sea de
ayuda y cualquier duda o comentario
que tengan no duden en publicar sus
comentarios en el blog.
Gracias a todos!

Potrebbero piacerti anche