0% encontró este documento útil (0 votos)
1 vistas

Java 2

Derechos de autor
© © All Rights Reserved
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
1 vistas

Java 2

Derechos de autor
© © All Rights Reserved
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 47

CLASE ANIDADA MIEMBRO(ATRIBUTO):

las clases anidadas son las que se encuentran dentro de otra clase y pasan a ser miembro cuando se
instancian y pasan a ser atributos de la clase principal:
CONVERSION IMPLICITA Y EXPLICITA:
CONVERCION IMPLICITA:

CONVERCION EXPLICITA:
CASTING:

BREAK AND CONTINUO:

El buscar es una etiqueta los dos puntos indica que lo siguiente hace parte del bloue de buscar.

El continuo lo que hace es que pasa a la siguiente iteracion.


GARBAGE COLLECTOR:
es un sistema en java para manejar y controlar los objetos, asi sabemos cuando un objeto se esta usando
se le asigna recursos y cuando no se esta usando eliminamos esos recursos para que otros objetos
utilicen estos recursos.

Entonces la linea 9 le estamos diciendo que estamos liberando la memoria del objeto
porque al decirle igual a null, es que un objeto siempre debe estar referenciado por una variable en el
momento en que este deja de ser referenciado en este caso cuando es igual a null, se activa el garbage
collector , esto lo que hara liberar la memoria que estaba usando.

El metodo de finalizar es la unica


forma de llamar el recolector antes
de que se termine la memoria.

Se utiliza para liberar los recursos


del sistema.

El System.gc, llama al recolector


AMBITOS:
ambito de clase:

ambito de metodo:

ambito de bloque:

CLASES ANONIMAS:
La clase anonima es una clase sin nombre y se define en la misma linea de codigo.
Se usa cuando necesitamos solamenete la
instancia de una clase.

Enum:
también podemos agregarle variables, métodos y constructores. El objetivo principal de enum es definir
nuestros propios tipos de datos (tipos de datos enumerados)

LINK SOBRE ENUM:


https://javadesdecero.es/avanzado/enumerados-enum-
ejemplos/
OPERADOR TERNARIO:
Es como un if.

CADENAS: INDEXOF – LASTINDEXOF:


comienza a buscar partir de la pocicion 20.
y el lastIndex comienza a buscar a final de la cadena.

O puede ir cadena.indexOf((int) ‘f’) me devuelve la posicion donde este.

CADENAS: STARTWITH – ENDWITH:


Para saber si comienza con o saber si
termina con.

CADENAS: REGIONMATHES:
se usa para comparar regiones de la cadena, ciertas secciones:

CADENAS: CHARAT – GETCHARS - SUBSTRING:


El primero nos da un elemento de la cadena y el segundo nosda una cantidad de caracteres de la
cadena.
Sub string me devuelve una cadena sin contar el primer elemento que se le indique y tomas el ultimo. O
puedo a partir de de una posicion tomar todos los demas de la cadena.

CLASE GENERICA:
nos permite utilizar codigo para usarlos con distintos tipos de objetos.

Definidas de forma generica no especifica el tipo de dato que se esta usando.


Este es el primer paso para volverlo generico.

como declarar y usar un metodo generico:

Si usamos
genericos en
lugar de
parametros de
tipo object
tendremos la
ventaja de que
los erros se
comprobaran en
tiempos de
copilacion.

E = elementos
k = claves
N = numeros
T = tipos
V = valores
HERENCIA EN GENERICOS Y WILDCARDS:
yo puedo asignarle a un objeto de un tipo un
objeto de otro tipo, como podemos ver en la
imagen si estos son compatibles.
Y si creo una lista de number puedo agregarle
elementos de tipo number o integer.

Entonces aca creamos un


arreglo que contendra
objetos de tipo Integer, pero
en el metodo de abajo yo le
digo que recibira un
ArrayList de numbers,
entonces si le paso a esto
el arrayList de Integer
mandara error y es
porque
este ArrayList de integer
no es un subtipo de
ArrayList de number .

Esto se usa con widcards las cuales representan un tipo desconocido y su simbolo es ?, entonces si en
el metodo le cambiamos el number por el ?, el metodo aceptara todo tipo de ArrayList

ahora la solucion no estaria lista porque el for recorre una lista de Number y como nosotros ahora
recibiremos un ArrayList que no sabemos de que tipo es, entonces para solucionar esto le podemos
decir a java que mi widcard sera de una subclase de otra:

con esto le podemos pasar cualquier tipo de arrayList siempre y cuando los objetos que contengan estos
extiendan de number. En las widcars el extends representa tanto el extends de herencia como el
implements de las interfaces.
Este tipo de widcards estan acotadas superiormente es decir aca le estamos diciendo que la clase debe
ser esa o una que extienda de ella.
Ahora hay widcards acotadas inferiormente en donde le decimos que el tipo de argumento es el
indicado o de una superclase del mismo, supongamos que el lista solo acepte Integer Number y Objetc:

EXCEPCIONES:

Se puede poner de esta manera pero comenzar con los mas especificos.

ahora al IOE excepciones como es una sibclase de Exception el segundo catch es inalcanzable porque
siempre se ejecutara el primero, por lo cual abrar un error en donde no se podra compilar.

FINALLY: el fnially simpre se ejecutara un ejemplo de esto es cuando abirmos un archivo y


necesitamos siempre cerrarlo.
NOTA: si en el try arroja una excepcion y el finally arroja
una excepcion entonces, se tomara la del finally y la del
try no la toma, para que tome la del try getSupreted() de
la exception que se propaga.
Esto que estamos
haciendo en el try e
estamosponiendo los
recursos que queremos
que se cierren, los
recursos es un objeto
que debe ser cerrado
cuando se ha
terminado de usar, cualquier objeto que implemente autoClosable esto incluye todos los que
implemente esto, pueden ser usado como recurso, el try con recursos hara que los recursos seran
cerrados despues de usarlos en el try.

Lanzar excepcion.

NOTA: UNO CREA SU PROPIA EXCEPTION SI:


• NECESITO UN TIPO DE EEXCEPCION QUE NO
ESTA REPRESENTADA EN L A PLATAFORMA
JAVA
• AYUDARE A LOS USUARIOS EL PODER
DIFERENCIAR MIS EXCEPCIONES DE LAS DE
TERCEROS
• MI CODIGO LANZA MAS DE UNA EXCEPCION
RELACIONADA
• SI USO UNA EXCEPCION DE OTRO MIS
USUARIOS TENDRAN ACCESO A LAS MISMAS.
SI RESPONDO QUE SI A UNO DE ESTAS SE DEBE
CREAR UNA EXCEPCION.

EXPRECIONES LABDA:
Se usan a partir de JAVA8. Se genera un codigo mas compacto y consiso.
Intefaz funcional: es un interfaz que solo tienen un metodo abstracto y solamente uno, este metodo
representa la intencion de la interfaz:
EJ: interfaz Runnable el cual solo tiene un metodo el cual es run. Este es otro ejemplo creado por
nosotros.
Entonces una exprecion labda es un metodo anonimo que no esta ligado a un identificador, las
interfaces funcionales son el objetivo perfecto de las expreciones labda, ya que sino asignamos una
exprecion labda a un objeto de este tipo el compilador no tendra ninguna duda de que nuestro objetivo
es su unico metodo abstractio.
EJ

Entonces aca conseguimos que nuestra exprecion labda se combierta en la implementacion de obtener
valor de la interfaz funcional mi valor.

EJ2:

----------------------------------------------------------------------------------------------------------------------------
NOTA: JAVA YA TIENE INTERFACES GENERICAS
PREDEFINIDAS QUE SATISFACEN CASI TODOS
LOS POSIBLES ESCENARIOS, MIRARLAS ANTES
DE CREAR NUESTRAS PROPIAS INTERFACES.
ESTAS INTERFACES FUNCIONALES PRE-
DEFINIDAS PERTENECEN A JAVA.UTIL.FUNCTION.

LAS INTERFACES FUNCIONALES PUEDEN TENER TAMBIEN METODOS ESTATICOS Y


PUEDEN TENER METODOS POR DEFECTO, TAMBIEN PUEDEN TERNER METODOS
PRIVADOS PERO SOLO PUEDEN TENER Y UNICAMENTE UNO ABSTRACTO.
ACA USAMOS UNA DEFINIDA POR JAVA.

CON LA ETIQUETA FUNTIONAL


INTERFACE LE DECIMOS QUE SERA UNA
INTERFAZ FUNCIONAL Y NO PERMITIRA
MAS DE UN METODO ABSTRACTO.

----------------------------------------------------------------------------------------------------------------------------
REGLAS Y BUENAS PRACTICAS LABDA:

hasta la linea 15 hiba todo bien, pero si


despues intento modificar estas variable
me botara un error, ya que una variable
usada en la exprecion labda debe ser
final. Esto solo sucede con la variables
locales y no las de instancia y no hace
falta declararlas como final pero para
efectos practicos deben serlo.
Entonces aca vemos que no bota error con las
variables de instancia, pero si con las locales y
no nos lo permite modificar. Las varibales
locales deben ser final. Las variables de
referencia, las que hacen referencia a objetos
y no a datos primitivos guardan la referencia
al objeto y no al objeto en si mismo, si
cambiamos el estado del objeto no bilaremos
la regla de que la variable sea final, peo no es
una buena practica.

El this dentro de labda hace referencia a la clase:

si hago esto me imprime es el atributo de la clase.

REFERENCIA A METODOS ESTATICOS:

Este metodo que suma dos enteros ya esta en una clase calculadora:

entonces yo sustituyo la exprecion labda por la referencia al metodo:


para hacer referencia a un metodo estatico, colocamos el nombre de la clase :: el nombreDelMetodo, el
metodo debe ser compatible con la interfaz funcional de la interfaz.

REFERENCIA A METODOS DE INSTANCIA DE OBJETOS:


Sabemos que los objetos de tipo String tienen un metodo llamado toUppercase que nos devuelve otro
String.

REFERENCIA A CONSTRUCTORES:

entonces el tipo y la cantidad de parametros del constructor deben concidir con los del metodo de la
interfaz funcional, y este metodo debe devolver un valor de la clase del constructor.
Nota:para conseguir codigo compacto y facil de leer usar referencia a metodos siempre que podamos.

USO LABDA:
el primer uso es la sustitucion de las clases anonimas que muchas veces resultan engorrosas, una de las
clases anonimas mas usadas es la clase Runnable:
veamos como cambia:
----------------------------------------------------------------------------------------------------------------------------
otra cosa que podemos hacer es iterar sobre colecciones usando expreciones labda:
el metodo forEach de la clase iterable tiene como parametro un objeto de tipo consumer que es una
interfaz funcional. El foreach lo que hace es ejecutar el metodo por cada objeto de la coleccion:

podemos sustituir la exprecion lamda por una referencia al metodo:

----------------------------------------------------------------------------------------------------------------------------
otra utilidad es la de comparacion, hasta java 8 teniamos que implementar una clase que implementara
comparator por cada tipo de comparacion que teniamos que hacer. EJ:
aca lo ordenamos por nombre:

para hacerlo mas compacto todabia podemos usar comparing de la clase comparator que fue
introducido en java8 y que acepta un parametro de tipo fuction, que lo que hace es extraer una clave
comparable y devolver un comparator que compare por esa clave:

es obtienen lo mismo.

----------------------------------------------------------------------------------------------------------------------------
puede simplificarse tambien en el manejo de eventos en entorno grafico:
STREAMS:
en la version 8 de java se introdujo una nueva abstracción para el procesamiento de colecciones los
STREAMS, con ellos java nos provee de una forma de trabajar con colecciones al estilo de la
programación funcional y con esto conseguimos mayor facilidad para desarrollar porgamas
concurrentes libres de errores, con un codigo mas limpio y lejible y una mayor asbtraccion en las
operaciones:

veamos como quedaria un codigo usando STREAMS comparandolo con un codigo clasico:

cliente tiene simplemente tres Strings como atributos, nombre, primer apellido y pais.

Entonces si quisieramos mostrar por pantalla los clientes de la lista cuyos nombres comienzan con M,
entonces si le agregamos condiciones a la lista, el codigo comenzaria a ensuciarse, tendriamos que
agregarle mas condiciones al if, o hacer if anidados empeorando la legibilidad y mantebilidad, veamos
como quedaria con Streams:

los objetos de la clase collection en java tienen un metodo llamado stream, que devuelve un stream con
los objetos de la coleccion:

a su vez los objetos de tipo stream tienen un metodo llamado filter que filtran los objetos que cumplen
un determinado predicado, este predicado acepta expreciones lambda:

y esto nos devolveria otro stream, ahora vamos a llamar a otro metodo del stream llamado forEach que
realiza una acción a cada uno de usus elementos:
con esto obtenemos el mismo resultado que con lo que se hizo arriba.
Nosotros podriamos agregar todos los filtros que quisieramos de manera muy sencilla:

esta funcionalidad de java tiene otra gran ventaja: nos permite de manera muy sencilla paralelizar las
acciones sobre los streams y solo es cambiar el metodo stream() por paralemStream(), cuando un
stream se ejecuta en paralelo java parte los streams es subStreams, los procesa en paralelo y luego
combina los resultados, esto afecta a la velocidad en la que la operacion se procesa, lo cual no siempre
sera mas rapida; valdra la pena usar paralelismo cuando estamos procesando grandes colecciones de
datos en maquinas multihilo:

si ejecutamos obtenemos el mismo resultado.

STREAMS: es una secuencia de elementos que se crea a partir de una fuente de datos como
colecciones, matrices, o recursos de entrada- salida; y que a ese stream se le aplican una serie de
operaciones concatenadas formando un piline que se pueden ejecutar en serie o en paralelo.

OPERACIONES INTERMEDIAS:
cuando trabajamos con streams operando sobre grupos de datos aplicamos una serie de operaciones
sobre los streams, las operaciones que aplicamos sobre un stream que produce otro stream se llaman
operaciones intermedias, estas operaciones no son ejecutadas hasta que no se invoca una operacion
terminal.
Veamos algunas operaciones intermedias mas interesantes que nos ofrece la clase stream:
para este ejemplo tenemos esta clase y la siguiente:

• veamos primero la operacion filter que devuelve un nuevo stream con los elementos que
cumplan con la condicion que indiquemos:
lo primero que haremos es hacer el stream a partir de la fuente de datos en este caso el
arrayList. Y para que se ejecuten las operaciones intermedias necesitaremos una operacion
terminal que produsca un resultado, en este caso usaremos la operacion terminal forEach para
mostrar el resultado:

• operacion peek:devuelve un stream con los elementos del stream y adicionalmente realiza una
accion a cada uno de estos elementos. Adicionalmente esto es muy util cuando estamos
trabajando con piblines muy largos para debuggear o logguear una parte intermedia del proceso;
por ejemoplo en el stream anterior queremos filtrar los clientes que comienzan por M y son de
mexico:
• operacion Map: que transforma los elementos de un Stream utilizando la funcion que le
pasemos(la funcion es un metodo que acepta un argumento y produce un resultado):

• operacion distinct: devuelve un string con los elementos no duplicados segun su metodo
equals:

el segundo pedro es eliminado.

• Operacion sorted: devuelve un String con los elementos ordenados, para ello los elementos
deben ser comparables:

OPERACIONES TERMINALES:
cuando trabajamos con streams nuestros piblines siempre deben terminar con una operacion terminal
que produce un resultado diferente a un Stream; veamos algunas:

tenemos este ejemplo, miraremos la primera operacion terminal:

• operacion forEach: realiza una accion a cada elemento del stream, en este caso se mostraran
por pantalla:
es comun que despues de aplicar nuestras operaciones intermedias a los conjuntos de datos, queramos
volverlos a tenerlos en forma de array para eso:

• operacion toArray:

• operacion collect: digamos que queremos guardar el resultado de las operaciones en otro
contenedor tenemos la operacion collect:

• operacion reduce: operacion de reduccion que toma una serie de elementos de entrada y los va
combinando aplicando repetidamente una operacion:

aca multiplicamos todos los elementos.

Hay operaciones de reduccion que ya estan implementadas en la clase Stream para usar directamente
por ejemplo:

• operacion sum: se aplica a un stream de tipo numerico:


NOTA: existen otras operaciones de reduccion implementadas como min, max, count..etc, para
conocerlas es interesante mirar las interfaces Stream, intStream , longStream, doubleStream.

Veamos operaciones que producen resultados booleanos:


• operacion anymatch: nos indica si alguno de los elementos del stream cumple una determinada
condicion, por ejemplo miremos si un numero es mayor que 10:

• operacion allMatch: hay otra operacion que nos dice si todos los elementos cumplen con la
condicion:

• operacion nonMatch: nos dice si ninguno de nuestros elementos cumple con la condicion:

CONCURRENCIA:
LOS HILOS EN JAVA:
Para sacar provecho de los procesadores hoy en dia es necesario el uso de multiples hilos en nuestros
programas, incluso aunque nosotros no lo hagamos de forma explicita algunas de las clases de librerias
de la plataforma java añadiran concurrencia a nuestros programas.

Una aplicacion java siempre tiene al menos un hilo, el hilo principal, y desde este hilo podemos
crear otros hilos.
Se pueden crear tantos hilos como queramos.
Si no le asignamos nombre a los hilos, java lo hara por nosotros, le pondremos nombre nosotros:
la forma que implementa la clase Runnable es mas felxible y mejor porque podemos usar objetos
de cualquier clase siempre que implemente la interfaz runnable.

METODOS DE LA CLASE THREAD:


Permiten controlar los hilos o conseguir informacion de estos:

este metodo lo que hace es que el hilo salude y diga su nombre.

Entonces veamos el metodo slep que pausa la ejecucion del hilo actual en unos milisegundos, hay un
metodo sobrecargado donde podemos indicarlo en milisegundos y nano segundos, el metodo sleep bota
un error de tipo interruptedException este error sale porque si otro hilo interrumpe a este mientras sleep
este activo:
NOTA: no existe garantia del que el tiempo de sleep sea preciso dependera del sistema donde
corra nuestra aplicacion y lo ocupado que este.

Veamos ahora como podemos hacer para interrumpir un hilo y hacer que haga otra cosa, el hilo que sea
interrumpido debe soportar su interrupcion y proveer las intrucciones que seran ejecutadas en caso de
ser interrumpido, en la mayoria de los casos lo que se hace esterimnar la ejecucion, para esto en este
caso que estamos usando sleep es meter el codigo en el catch:

si no se tubiera la llamada a thread sleep tocaria preguntarle periodicamente si se ha interumpido


este es un ejemplo.

por otro lado podriamos preguntar si nuestro hilo sigue vivo, este sigue vivo si tienen algo que ejecutar,
y al final de nuestro programa podriamos querer liberar recurso para ellos hariamos:

una manera mas sencilla es usar join al hilo del que se esta esperando.
ERRORES DE LA INTERACCION ENTRE HILOS:
Estos son errores que ocurren cuando usamos concurrencia:

interferencia entre hilos: ocurre cuando se realizan al mismo tiempo dos o mas operaciones sobre un
mismo dato en diferentes hilos intercalandose entre si. El problema ocurre cuando estas operaciones
constan de varias instrucciones y estas se solapan.

errores de consistencia de memoria: se producen cuando dos hilos tienen versiones diferentes de lo
que tendria que hacer el mismo dato.

Vemoas un ejemplo

imaginemos que dos hilos estan actuando sobre un mismo objeto de tipo contador, supongamos que
ambos hilos llaman al metodo incrementar y que aca no sucedera nada extraño que cada uno
incrementarane un la varible, pero no es asi aca el metodo tiene mas de un paso, la maquina virtual de
java la divide en tres pasos:
entonces ca suponemos que los hilos se ejecutaron al mismo tiempo, y aca estamos viendo que estamos
perdiendo una unidad lo cual podria ser catastrofico en lagunos casos.
Estos errores son dificiles de detectar y solo ocurriran aveces.

Cuando trabajamos con variables no volatiles la maquina virtual de java puede optar por copiar
variables de la memoria principal a la cache de la CPU mientras trabaja con ellas para mejorar el
rendimiento, si la maquina contiene mas de una CPU cada hilo podria correr en una diferente y hacer la
copia en su diferente cache, nosotros no tenemos control de cuando la maquina virtual de java copiara
la variable a la cache de la CPU y cuando decide volver a pasarla a la memoria principal, esto hace que
las modificaciones de un hilo no se vean por el otro:

estos problemas son contraintiutivos y sus causas complejas:

supongamos que partimos con la variable c a 0, si desde el hilo a incrementamos el valor del contador y
despues desde ese mismo hilo lo imprimimos veremos el valor y sera , ahora si lo incementamos en el
hilo a y lo vemos en el hilo b no tenemos garantia de que el valor de contador sea 1:
para segurarnos de que un hilo vea las modificaciones de otro debemos garantizar una relacion
conocida como appendsBefore, veamos que mecanismo nos asegura tener esta relacion:
• cada accion en un hilo ocurre antes que las acciones en ese mismo hilo que bienen despues en el
orden del programa.
• La sincronizacion es el primer mecanismo que nos permite evitar los problemas de interaccion
con hilos, con ella lo que hacemos es bloquear una variable para que solo un hilo pueda
modificarla y desbloquearla cuando la accion ha terminado, el desboqueo ocurre antes que
cualquier bloqueo subsiguiente y en concecuencia cualquier accion de antes del desbloqueo
tendra una relacion de appendsBefore con las que ocurran despues del bloqueo.

• Las variables volatile garantizan que su escritura y lectura sean hechas desde la memoria
principal, la escritura a una variable volatile ocurre antes que cualquier escritura o lectura
subsecuente de la misma varible, por lo tanto el problema anterior se evita haciendo la variable
volatile pero siempre con precaucion porque esto podria afectar el rendimiento del programa, ya
que suele ser mas costoso leer en la memoria pricipal.
• Una llamada start en un hilo ocurre antes que cualquier accion en ese hilo; todas las acciones en
un hilo ocurren antes que una llamada exitosa al metodo join de ese hilo; los metodos de clases
del paquete java.util.concurrent y sus subpaquetes tienen mecanismos que extienden estas
garantias de relacion appendsBefore.
METODOS SINCRONIZADOS:
los problemas de interferencia de hilos y los errores de inconsistencia de memoria producidos al
interactuar mas de un hilo se pueden eliminar mediante la sincronizacion, con la sincronizacion
conseguimos poner orden en como los hilos acceden a los recursos compartidos, una forma de
sincronizacion son los metodos sincronizados, con ellos conseguimos bloquear un objeto para que solo
un hilo pueda usar pueda usar sus metodos sincronizados.

Tenemos el siguiente ejemplo:

esta clase no es segura con hilos ya que si dos hilos llaman al


mismo tiempo a uno de los dos primeros metodos, se puede
producri un solapado de ls operaciones y llegar a errores
inesperados, tambien puede existir incosnistencia en el valor
que dos hilos vena para la variable C.

para evitar estos problemas podemos realizar los metodos incrementar y decrementar sincronizados,
para ello solo añadimos la palabra synchronized, para ver los efecto de la sincronizacion vamos a
hacer que el metodo incrementar ademas de incrementar el contador espere 5 segundos:

ahora vamos a usar este contador en varios hilos:


Entonces lo que corrira cuando lo corramos es que todos los objetos en java tienen un monitor que
controla el acceso al objeto, cuando un hilo llama un metodo sincronizado bloquea el acceso al objeto,
esto quiere decir que otro hilo que llame un metodo sincronizado de este objeto tendra que esperar que
este se desbloquee, cuando el primer hilo termine de ejecutar el metodo sincronizado el monitor
desbloquera el objeto permitiendo que sea usado por otro hilo.

El desbloqueo del del objeto asegura que todo lo ocurrido antes del desbloqueo ocurre antes de lo
ocurrido despues, y por lo tanto evitamos errores de inconsistencia de memoria.

SENTENCIAS SINCRONIZADAS:
La sincronizacion de metodos es muy util y efectiva a la hora describir una clase segura, pero cuando
usamos clases creadas por otros que no sabemos si son seguras simplemente debemos llamar a los
metodos de estos objetos dentro de un bloque sincronizado.
Veamos un ejemplo:
entonces aca lo que hacemos es sincronizar el bloque de sentencias que realizan llamadas a metodos del
objeto contador, para sincronizar debemos especificar el objeto al cual queremos bloquear y lo
pondremos entre parentesis como se ve arriba. Y se añaden las instrucciones que se quieren sincronizar.
PROBLEMAS DE SINCRONIZACION:
la sincronización no resuelve los problemas que nos trae la interaccion de varios hilos sobre los mismo
recursos, para ello hacemos que un hilo tenga que esperar a otro hilo libere el recurso antes de
continuar su ejecucion, esto trae sus propios problemas, ya que realentiza la ejecucion de los hilos
pudiendo incluso dejandlos bloqueados para siempre.

• Uno de los problemas que mas nos podemos encontrar es el dead lock o punto muerto, en esta
situacion dos o mas hilos estan bloqueados para siempre esperando a que otro haga algo,
veamos un ejemplo:

imaginemos que ana y pablo estan en una conferencia en la que de vez en cuando quieren tomar
notas peros olo hay un cuaderno y un boligrafo.

Entonces pablo hace lo mismo pero el primero toma el boligrafo y luego quiere tomar el cuaderno, al
ejecutarlo sucede esto:
entonces lo que se hace es que cuando los libere ana pablo tendra el cuaderno y el boligrafo.

• Otro problema es el live block en esta situacion un hilo actua en respuesta a la accion o estado
de otro hilo, si esta accion depende a su vez de una accion del primer u otro hilo se produce un
bloqueo que les impide continuar, el ejemplo mas comun es cuando dos personas se encuentran
de frente y no se dejan pasar y se mueven al mismo lado y no se dejan pasar.

• Y el otro problema es la inanicion es cuando a un hilo se le dificulta acceder a un recurso


compartido, esto ocurre porque otro hilo con mayor prioridad esta usando el recurso o hay
muchos hilos compitiendo por el mismo.

OBJETOS INMUTABLES:
La mejor estrategia para asegurarnos de que nuestro codigo es seguro en cuanto a concurrencia es
diseñar nuestros objetos como inmutables, de esta forma nos asegurarnos de que dos hilos no pueden
modificar al mismo tiempo y evitariamos el problema de que un hilo lo modifique sin que otro vea la
modificacion; tendemos a no crear objetos inmutables pensando que estos les reste eficiencia a los
programas, pero este impacto suele estar sobreestimado.

Entonces tenemos la clase circulo que tiene dos variables de instancia, radio y color, en donde color es
una variable de referencia ya que contiene la referencia a un objeto. Y tenemos el constructor y
tenemos los respectivos getters y setters.
Vamos a ver como transformar este objeto a inmutable. Primero es asegurarnos de no tener metodos
que permitan modificar el estado del objeto, el estado del objeto esta representado por sus variables de
instancia, en nuestro caso tenemos tres metodos que lo incumplen, establecerRadio, establecerColor y
escalar a este se adatara para que el metodo devuelva un objeto de tipo circulo como el actual pero
escalado:

lo siguiente es que todas las variables de instancia sean private e final, de tal forma de que no se puedan
modifcar el estado, accediendo directamente a las variables desde el exterior, ademas el al ser final sera
el valor que se mantenga durante toda la ejecucion.
Despues de esto es que no se tenga acceso desde fuera a la referencia de nuestras variables de
referencia que contienen objetos mutables, en este caso la clase color, en esta caso color esta diseñada
como inmutable pero vamos a mirarla como si no lo fuera, entonces en el metodo
estamos devolviendo la referencia del
objeto color, le estamos dando acceso
completo. Entonces vamos a hacer una
copia defenciba que es elo que
devolveremos:

con esto no devolvemos nuestro color sino un color igual al nuestro que sirve como informacion pero
no sirve para transformarlo. Ocurre lo mismo con nuestro constructor, estamos aceptando como color
un objeto que ha sido creado fuera de nuestra clase y por lo tanto tienen acceso total a el

lo otro es que no se pueda extender la clase para esto podemos hacer la clase final,y asi no se
reescribiran los metodos. O podemos hacer el constructor privado y sustituirlos por metodos estaticos
factoria:
LA INTERFAZ LOCK:
La interfaz lock nos permite implementar objetos que puedan ser usados como medio de control de
acceso a recursos compartidos por hilos, es el mismo mecanismo que nos ofrece la sincronizacion pero
mucho mas flexible; ya que por ejemplo nos permite que un hilo se heche atras cuando intenta bloquear
un objeto que esta bloqueado por otro hilo, y puede hacerlo inmediatamente o despues de un tiempo de
espera; tambien nos permite bloquear un objeto de manera que el bloqueo pueda ser interrumpido.
Esta mayor flexibilidad trae con sigo una mayor responsabilidad ya que debemos que asegurarnos de
siempre liberar los recursos.

Veamos como usar lock:


Entonces vemos que en la clase impresora tenemos un objeto de tipo Lock, pero que instancia
ReentrantLock que implementa Lock pero tiene mas metodos pero se asemeja mas al bloqueo por
sincronización.

Lo que hace el metodo imprimir es bloquear el objeto de tipo Lock y simular una impresion que dura
un tiempo aleatorio hasta 10 segundos.
Entonces debemos ver la estructura que se usa comun mente, cuando bloqueamos un objeto de tipo
lock debemos asegurarnos de desbloquearlo bajo cualquier circunstancia y por lo tanto debemos usar
un bloque try y finally, en el finally desbloquearemos el objeto.
Cuando se bloquean varios objetosdebemos librerarlos en orden opuesto.
Vamos a ver que varios hilos compitan por el recurso de una impresora:

otra posibilidad que nos da la interfaz lock es uno de sus metodos que es tryLock, que tiene dos
posibilidades, en la version sin parametros se intentara adquirir el bloqueo solo si esta libre en ese
momento en su version con parametros lo que hace es intentar bloquear el objeto dando un margen de
tiempo de espera para ver si es desbloqueado si es que esta bloqueado en ese momento. Vamos a
probarlo:

veamos que nuestra impresora tiene dos vias de impresion que pueden trabajar concurrententemente
donde una es mas lenta pero de mayor calidad y la otra mas rapida pero de menor calidad; sino
conseguimos acceder a la impresion de calidad hacemos la impresion mas mala:
ahora veamos el tryLock dando el tiempo de espera.

Este metodo da una excepcion entonces solo es meterlo en el bloque try-catch.

Ahora vemos que todos los hilos que han accedido al recursos antes de 10 segundos desde el inicio de
la ejecucion han hido por la via de calidad el resto por la via rapida.

EJECUTORES:
en java cuando creamos un hilo directamente, lo estamos asociando irremediablemente con la tarea que
debe ejecutar, con los ejecutores lo que conseguimos es dissociar estos dos conceptos consiguiendo un
codigo mucho mas flexible. Java tien una interfaz llamada execute, esta interfaz tienen un unico
metodo pensado para lanzar una tarea especificada por un runnable:

de esta interfaz extienden otras dos :

estas añaden varias funcionalidades entre ellas aceptar un callable en vez de un runnable, lo que nos
permite que la tarea devuelva un valor. Tambien nos devuelve un objeto de tipo future que nos permite
comprobar el estado de la tarea y acceder al valor devuelto por el callable, la interfaz
ScheduledExecutorService nos permite programar la tarea para que sea ejecutada en el futuro e incluso
periodicamente.

Nosotros podemos implementar nuestros propios executors:

pero java tiene ya varios implementados que cubren muchos escenarios, podemos acceder a ellos a
travez de la clase executors:
como se puede ver esta clase tiene varios metodos estaticos factory y que nos devuelve diferentes
executers, vamos a ver un ejemplo sencillo con:

aca tenemos un runnable que solo saluda y muestra el nombre del hilo, veamos la forma en que
manualmente creariamos un hilo con la tera y lo lanzariamos:
veamos como hacerlo con un ejecutor:
primero creamos un ejecutor, y llamamos a su metodo submit con la tarea que queremos que ejecute:

se puede usar todas las veces que queramos con la misma o diferentes tareas.

Nuestro ejecutor dispone


de un unico hilo para ejecutar los procesos que le ordenemos pero podriamos a ver creado un ejecutor
con un numero determinado de hilos para atender a nuestras tareas.
Vamos a crear uno con dos hilos:
si ejecutamos vemos que aveces realiza la tarea con el hilo uno y otras con el hilo dos, dependiendo de
su disponibilidad pero lo bueno es que todo esto se maneja de forma interna y nosotros no debemos
que preocuparnos de nada, estos hilos que fueron creados para este ejecutor se mantienen vivos en todo
momento, esperando a recibir nuevas tareas, en el momento en que ya no los necesitemos debemos
llamar al metodo shutDown del ejecutor. Este metodo a que las tareas mandadas previamente terminen,
pero no permite mandar nuevas tareas:

si queremos que termine de ejecutar cualquier tarea en curso le damos shutdownNow().

Veamos como usar callables:


simula hacer un calculo muy largo
y lento y devolvemos un resultado.

El metodo submit del executor nos devuelve un objeto de tipo future(generico) en nuestro caso sera de
tipo integer, porque nuestro callable devuelve un Intger:

en el momento de llamar a submit no tendremos todavia el resultado ya que es un calculo muy costoso,
y para saber si la tarea se ha completado podemos usar el metodo isDone de future:
si llamamos al metodo getFuture conseguiremos que el hilo actual se bloquee hasta que la tarea se halla
terminado:

el get le da tiempo de que termine la tarea.

NOTA: EN GRANDES PROGRAMAS SE USA SOLO


EJECUTORES EN LUGAR DE CREAR HILOS
DIRECTAMENTE, YA QUE CON ELLO
CONSEGUIREMOS UN CODIGO MUCHO MAS
FLEXIBLE Y QUE PODREMOS AMPLIAR EN EL
FUTURO.

También podría gustarte