Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Standard Edition
Nivel IV
. Sockets..........................................................................................39
2.1 Fundamentos..........................................................................39
2.3 Introducción............................................................................42
Socket..................................................................................51
ServerSocket........................................................................51
DatagramSocket..................................................................51
i
DatagramPacket..................................................................52
MulticastSocket....................................................................52
NetworkServer.....................................................................52
NetworkClient.......................................................................52
SocketImpl...........................................................................52
2.9.3 Ejecución..........................................................................59
ii
1. Concurrencia
En esencia la multitarea nos permite ejecutar varios
procesos a la vez; es decir, de forma concurrente y por
tanto eso nos permite hacer programas que se ejecuten en
menor tiempo y sean más eficientes. Evidentemente no
podemos ejecutar infinitos procesos de forma concurrente
ya que el hardware tiene sus limitaciones, pero raro es a
día de hoy los ordenadores que no tengan más de un
núcleo por tanto en un procesador con dos núcleos se
podrían ejecutar dos procesos a la vez y así nuestro
programa utilizaría al máximo los recursos hardware. Para
ejemplificar la diferencia entre procesos secuenciales y
paralelos mostraremos un par de imágenes, supongamos
que tenemos un programa secuencial en el que se han de
ejecutar 4 procesos; uno detrás de otro, y estos tardan
unos segundos:
1
Si en vez de hacerlo de forma secuencial, lo hiciésemos
con 4 hilos, el programa tardaría en ejecutarse solo 20
segundos, es decir el tiempo que tardaría en ejecutarse el
proceso más largo. Esto evidentemente sería lo ideal, pero
la realidad es que no todo se puede paralelizar y hay que
saber el número de procesos en paralelo que podemos
lanzar de forma eficiente. En principio en esta entrada no
vamos a hablar sobre ello ya que el objetivo de la misma es
ver como se utilizan los hilos en java con un ejemplo
relativamente sencillo y didáctico.
2
1.1 Qué es un Thread
La Máquina Virtual Java (JVM) es un sistema multihilo.
Es decir, es capaz de ejecutar varios hilos de ejecución
simultáneamente. La JVM gestiona todos los detalles,
asignación de tiempos de ejecución, prioridades, etc., de
forma similar a como gestiona un Sistema Operativo
múltiples procesos. La diferencia básica entre un proceso
de Sistema Operativo y un Thread Java es que los hilos
corren dentro de la JVM, que es un proceso del Sistema
Operativo y por tanto comparten todos los recursos,
incluida la memoria y las variables y objetos allí definidos. A
este tipo de procesos donde se comparte los recursos se
les llama a veces procesos ligeros (lightweight process).
Java da soporte al concepto de Thread desde el propio
lenguaje, con algunas clases e interfaces definidas en el
paquete java.lang y con métodos específicos para la
manipulación de Threads en la clase Object. Desde el
punto de vista de las aplicaciones los hilos son útiles
porque permiten que el flujo del programa sea divido en
dos o más partes, cada una ocupándose de alguna tarea
de forma independiente. Por ejemplo un hilo puede
encargarse de la comunicación con el usuario, mientras
que otros actúan en segundo plano, realizando la
transmisión de un fichero, accediendo a recursos del
sistema (cargar sonidos, leer ficheros ...), etc. De hecho,
3
todos los programas con interface gráfico (AWT o Swing)
son multihilo porque los eventos y las rutinas de dibujado
de las ventanas corren en un hilo distinto al principal.
super(str);
new ThreadEjemplo("Fulano").start();
new ThreadEjemplo("Mengano").start();
5
Ejecutando varias veces el programa, se podrá observar
que no siempre se ejecuta igual.
6
1.3. La interface Runnable
La interface Runnable proporciona un método
alternativo a la utilización de la clase Thread, para los
casos en los que no es posible hacer que la clase definida
extienda la clase Thread. Esto ocurre cuando dicha clase,
que se desea ejecutar en un hilo independiente deba
extender alguna otra clase. Dado que no existe herencia
múltiple, la citada clase no puede extender a la vez la clase
Thread y otra más. En este caso, la clase debe implantar la
interface Runnable, variando ligeramente la forma en que
se crean e inician los nuevos hilos.
Thread.currentThread().getName());
Thread.currentThread().getName());
7
public static void main (String [] args) {
"Mengano").start();
8
thread.start();
9
1.4. El ciclo de vida de un Thread
El gráfico resume el ciclo de vida de un thread:
10
Cuando se instancia la clase Thread (o una subclase) se
crea un nuevo Thread que está en su estado inicial ('New
Thread' en el gráfico). En este estado es simplemente un
objeto más. No existe todavía el thread en ejecución. El
único método que puede invocarse sobre él es el método
start().
11
de espera finalice. Durante este tiempo el sistema puede
ceder control a otros hilos activos.
12
(entremezclándose sus éstas). El mecanismo por el cual un
sistema controla la ejecución concurrente de procesos se
llama planificación (scheduling). Java soporta un
mecanismo simple denominado planificación por prioridad
fija (fixed priority scheduling). Esto significa que la
planificación de los hilos se realiza en base a la prioridad
relativa de un hilo frente a las prioridades de otros.
13
Si dos o más hilos están listos para ejecutarse y tienen
la misma prioridad, la máquina virtual va cediendo control
de forma cíclica (round-robin). El hecho de que un hilo con
una prioridad más alta interrumpa a otro se denomina se
denomina 'planificación apropiativa' (preemptive
scheduling).
14
ningún conflicto, dado que no comparten nada. Sin
embargo, hay ocasiones que distintos hilos en un programa
sí necesitan establecer alguna relación entre sí, o compartir
objetos. Se necesita entonces algún mecanismo que
permita sincronizar hilos, así como, establecer unas 'reglas
del juego' para acceder a recursos (objetos) compartidos.
contenedor = c;
contenedor.put(i);
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
16
private Contenedor contenedor;
contenedor= c;
int value = 0;
value = contenedor.get();
17
Productor y Consumidor se usarían desde un método
main de la siguiente forma:
produce.start();
consume.start();
18
public class ContainerProduct {
try {
wait();
} catch (InterruptedException e) {
hayDato = false;
notifyAll();
return dato;
19
public synchronized void put(int valor) {
try {
wait();
} catch (InterruptedException e) { }
dato = valor;
hayDato = true;
notifyAll();
20
En el método put, antes de almacenar el valor en dato
hay que asegurarse de que el valor anterior ha sido
consumido. Si todavía hay valor (hayDato es true) se
suspende la ejecución del hilo mediante el método wait.
Invocando wait (que es un método de la clase Object) se
suspende el hilo indefinidamente hasta que alguien le envíe
una 'señal' con el método notify o notifyAll. Cuando esto se
produce (en este caso, la señalización mediante notify lo
produce el método get) el método continúa, asume que el
dato ya se ha consumido, almacena el valor en dato y
envía a su vez un notifyAll para notificar a su vez que hay
un dato disponible.
21
cambiando las variables miembro dato y hayDato, el
consumidor llamara al método get y este a su vez
empezara a cambiar estos valores podrían producirse
resultados inesperados (este ejemplo es sencillo, pero
fácilmente pueden imaginarse otras situaciones más
complejas).
22
• Los métodos synchronized son más costosos en el
sentido de que adquirir y liberar los bloqueos consume
tiempo (este es el motivo por el que no están sincronizados
por defecto todos los métodos).
23
tenemos un array con [1,3,5] significará que el cliente ha
comprado 3 productos y que la cajera tardara en procesar
el producto 1 ‘1 segundo’, el producto 2 ‘3 segundos’ y el
producto 3 en ‘5 segundos’, con lo cual tardara en cobrar al
cliente toda su compra ‘9 segundos’.
Solución secuencial:
Clase “Cajera.java“:
24
cliente.getNombre() + " EN EL TIEMPO: " +
(System.currentTimeMillis() - timeStamp) / 1000 +"seg");
this.esperarXsegundos(cliente.getCarroCompra(
)[i]);
try {
25
Thread.sleep(segundos * 1000);
Thread.currentThread().interrupt();
Clase “Cliente.java“:
26
el tiempo del Cliente 1 + Cliente 2. A continuación vamos a
ver como programamos el método Main para lanzar el
programa. CUIDADO: Aunque hayamos puesto dos
objetos de la clase Cajera (cajera1 y cajera2) no significa
que tengamos dos cajeras independientes, lo que estamos
diciendo es que dentro del mismo hilo se ejecute primero
los métodos de la cajera1 y después los métodos de la
cajera2, por tanto, a nivel de procesamiento es como si
tuviésemos una sola cajera
Clase “Main.java“:
27
Cajera cajera2 = new Cajera("Cajera 2");
cajera1.procesarCompra(cliente1, initialTime);
cajera2.procesarCompra(cliente2, initialTime);
28
La cajera Cajera 1 HA TERMINADO DE PROCESAR
Cliente 1 EN EL TIEMPO: 15seg
29
¿Y si en vez de procesar primero un cliente y después
otro, procesásemos los dos a la vez?, ¿Cuánto tardaría el
programa en ejecutarse? Pues bien, si en vez de haber
solo una Cajera (es decir un solo hilo), hubiese dos Cajeras
(es decir dos hilos o threads) podríamos procesar los dos
clientes a la vez y tardar menos tiempo en ejecutarse el
programa. Para ello debemos de modificar la clase
“Cajera.java” y hacer que esta clase herede de la
clase Thread para heredar y sobre-escribir algunos de sus
métodos. Primero vamos a ver como codificamos esta
nueva clase “CajeraThread.java” y después explicamos
sus características.
30
@Override
"seg");
this.cliente.getCarroCompra().length; i++) {
this.esperarXsegundos(cliente.getCarroCompra
i
()[ ]);
"seg");
31
}
try {
Thread.sleep(segundos * 1000);
Thread.currentThread().interrupt();
33
CajeraThread cajera2 = new
CajeraThread("Cajera 2", cliente2, initialTime);
cajera1.start();
cajera2.start();
34
Procesado el producto 2 del cliente Cliente 2->Tiempo:
4seg
35
La cajera Cajera 1 HA TERMINADO DE PROCESAR
Cliente 1 EN EL TIEMPO: 15seg
36
solo vamos a tener que sobre-escribir el método “run()“. En
este caso solo será necesario implementar el método
“run()” para que los procesos implementados en ese
método se ejecuten en un hilo diferente. Vamos a ver un
ejemplo de cómo utilizando objetos de las clases
“Cliente.java” y “Cajera.java” podemos implementar la
multitarea en la misma clase donde se llama al método
Main de la aplicación. A continuación vemos la codificación
en la clase “MainRunnable.java“:
this.cajera = cajera;
this.cliente = cliente;
37
this.initialTime = initialTime;
new Thread(proceso1).start();
new Thread(proceso2).start();
38
@Override
this.cajera.procesarCompra(this.cliente,
this.initialTime);
39
Procesado el producto 1 del cliente Cliente 2->Tiempo:
1seg
40
Procesado el producto 5 del cliente Cliente 1->Tiempo:
12seg
41
. Sockets
2.1 Fundamentos
Los sockets son un sistema de comunicación entre
procesos de diferentes máquinas de una red. Más
exactamente, un socket es un punto de comunicación por
el cual un proceso puede emitir o recibir información.
42
2.2 Funcionamiento genérico
Normalmente, un servidor se ejecuta sobre una
computadora específica y tiene un socket que responde en
un puerto específico. El servidor únicamente espera,
escuchando a través del socket a que un cliente haga una
petición.
44
2.3 Introducción
El paquete java.net de la plataforma Java proporciona
una clase Socket, la cual implementa una de las partes de
la comunicación bidireccional entre un programa Java y
otro programa en la red.
45
El servidor establece un puerto y espera durante un
cierto tiempo (timeout segundos), a que el cliente
establezca la conexión.
Cuando el cliente solicite una conexión, el servidor
abrirá la conexión socket con el método accept().
El cliente establece una conexión con la máquina host
a través del puerto que se designe en puerto # ·
Socket miCliente;
46
miCliente = new Socket( "maquina", numeroPuerto );
Socket miCliente;
try {
miCliente = new
Socket( "maquina",numeroPuerto );
} catch( IOException e ) {
47
System.out.println( e );
try {
socketServicio = miServicio.accept();
} catch( IOException e ) {
System.out.println(e);
48
2.6 Creación de Streams
2.6.1 Creación de Streams de Entrada
En la parte CLIENTE de la aplicación, se puede utilizar
la clase DataInputStream para crear un stream de entrada
que esté listo a recibir todas las respuestas que el servidor
le envíe.
DataInputStream entrada;
try {
entrada = new
DataInputStream( miCliente.getInputStream() );
} catch( IOException e ) {
System.out.println( e );
49
En el lado del SERVIDOR, también usaremos
DataInputStream, pero en este caso para recibir las
entradas que se produzcan de los clientes que se hayan
conectado:
DataInputStream entrada;
try {
entrada = new
DataInputStream( socketServicio.getInputStream() );
} catch( IOException e ) {
System.out.println( e );
PrintStream salida;
try {
salida = new
PrintStream( miCliente.getOutputStream() );
50
} catch( IOException e ) {
System.out.println( e );
DataOutputStream salida;
try {
salida = new
DataOutputStream( miCliente.getOutputStream() );
} catch( IOException e ) {
System.out.println( e );
PrintStream salida;
try {
salida = new
PrintStream( socketServicio.getOutputStream() );
} catch( IOException e ) {
System.out.println( e );
try {
salida.close();
entrada.close();
miCliente.close();
52
} catch( IOException e ) {
System.out.println( e );
try {
salida.close();
entrada.close();
socketServicio.close();
miServicio.close();
} catch( IOException e ) {
System.out.println( e );
53
2.8 Clases útiles en comunicaciones
Socket
ServerSocket
54
DatagramSocket
DatagramPacket
MulticastSocket
NetworkServer
55
NetworkClient
SocketImpl
56
2.9 Ejemplo de uso
Para comprender el funcionamiento de los sockets no
hay nada mejor que estudiar un ejemplo. El que a
continuación se presenta establece un pequeño diálogo
entre un programa servidor y sus clientes, que
intercambiarán cadenas de información.
import java.io.DataInputStream;
import java.io.InputStream;
57
import java.net.Socket;
public Cliente( ) {
try{
PUERTO );
InputStream aux =
skCliente.getInputStream();
aux );
System.out.println( flujo.readUTF() );
skCliente.close();
} catch( Exception e ) {
System.out.println( e.getMessage() );
58
}
public static void main( String[] arg ) {
new Cliente();
}
59
2.9.2 Programa Servidor
El programa servidor se instala en un puerto
determinado, a la espera de conexiones, a las que tratará
mediante un segundo socket.
60
Tras atender cuatro clientes, el servidor deja de ofrecer
su servicio:
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public Servidor( ) {
try{
ServerSocket(PUERTO);
PUERTO );
// Crea objeto
61
System.out.println("Sirvo al cliente " +
numCli);
OutputStream aux =
skCliente.getOutputStream();
DataOutputStream( aux );
skCliente.close();
System.out.println("Demasiados clientes
por
hoy");
} catch( Exception e ) {
System.out.println( e.getMessage() );
}
public static void main( String[] arg ) {
62
new Servidor();
}
}
63
2.9.3 Ejecución
Aunque la ejecución de los sockets está diseñada para
trabajar con ordenadores en red, en sistemas operativos
multitarea (por ejemplo, Windows y UNIX) se puede probar
el correcto funcionamiento de un programa de sockets en
una misma máquina.
java cliente:
Hola cliente 1
java cliente:
Hola cliente 2
java cliente:
Hola cliente 3
Sirvo al cliente 1
Sirvo al cliente 2
Sirvo al cliente 3
64
Demasiados clientes por hoy
66
drivers de bases de datos como lo podemos observar en la
siguiente figura:
67
3.1 Paquete java.sql
Uso de controladores de las Base de Datos.
Interfaz de aplicación:
68
o Savepoint: puntos de recuperación en una
transacción.
Recuperación de los resultados
o ResultSet: conjunto de resultados que retorna la
ejecución de una consulta (query).
o ResultSetMetaData: información sobre
columnas del objeto ResultSet.
Connection
o Representa la sesión con la conexión a la base
de datos.
o La función principal de Connection es crear
objetos del tipo Statement (Statement,
PreparedStatement, CallableStatement).
o Cómo de crea:
Connection conn =
DriverManager.getConnection(url);
Connection conn =
DriverManager.getConnection(url, user,
password);
o Características transaccionales:
conn.SetAutocommit(false/true);
69
con.commit();
conn.rollback();
Statement
o Es el canal a través del cual se le envían
instrucciones SQL a la base de datos y se
reciben los resultados.
o Las instrucciones SQL puede ser instrucciones
DML (Insert, Update, Delete), DDL (Create, Drop)
o instrucciones SELECT.
o Como se crea:
Statement stmt= conn.createStatement();
o Sobre una conexión se pueden tener n objetos
Statement.
o Métodos de ejecución:
Ejecución de instrucciones SELECT
ResultSet resultadoSelect =
stmt.executeQuery(sql);
Ejecución de instrucciones DML/DDL
int resultadoDML =
stmt.executeUpdate(sql);
70
o Permite el manejo de parámetros dinámicos
o Ventajas técnicas sobre Statement:
Si se tiene Statement la instrucción SQL es
compilada cada vez que se usa.
Con PreparedStatement solo compila una
vez.
o Como se crea:
PreparedStatement stmt=
conn.prepareStatement(sql);
o Ejemplo:
sql = “SELECT * FROM productos WHERE
id=? AND fecha=?”
Parámetro 1:id
Parámetro 2: fecha
PreparedStatement stmt=
conn.prepareStatement(sql);
Pasando parámetros:
stmt.setInt(1,“10”); //Si quisiéramos el
producto 10
stmt.setDate(2,“03/12/2016”) //Los
productos creados en esa
fecha.
ResultSet
71
o Tiene el mismo comportamiento de un cursor
o Define los métodos, que permiten acceder al
cursor generado como resultado de la ejecución
de un SELECT.
o El puntero está ubicado antes de la primera fila.
o Para moverse entre filas se emplea
ResultSet.next()
o Para obtener una columna especifica de la fila,
se puede hacer invocando el método
ResultSet.getXXX (xxx indica el tipo de datos)
o Ejemplo:
String sql=“select * from productos”
Statement stmt_ = conn.createStatemet();
ResultSet resultado_ = stmt_.executeQuery(sql);
while(resultado_.next()){
System.out.println(“ id producto-->
”+resultado_.getInt(“id”));
System.out.println(“ producto-->
”+resultado_.getString(“nombre”));
72
3.2 Correspondencia de los tipos SQL con clases o
interfaces en Java
73
3.2.1 Secuencia normal de flujo
1. Establecer la conexión con la Base de Datos
a. Cargar controladores
b. Establecer la conexión
2. Crear un objeto Statement para hacer petición a la
Base de Datos
a. Asociar una sentencia SQL al objeto Statement
b. Proporcionar valores de los parámetros
c. Ejecutar el objeto Statement
3. Procesar los resultados
4. Liberar recursos (cerrar la conexión)
74
3.2.2 Secuencia normal para la ejecución de varias
instrucciones dentro de una transacción
1. Abrir transacción
a. Crear y ejecutar instrucciones.
b. Procesar resultados.
2. Cerrar transacción
Registrar un controlador
o Los DriverManager se encargan de gestionar
la conexión y todas las comunicaciones con la
Base de Datos.
o Necesitan conocer los controladores
específicos para las BD que se vayan a
utilizar.
Ejemplo: Registro de un controlador
o Utilizar el controlador dependiendo los
Sistemas administradores de bases de datos a
utilizar en el desarrollo de la aplicación.
o Estos controladores se pueden instalar
directamente.
o También se pueden registrar con el Class
Loader de Java.
Ejemplo de registro de un driver de Mysql:
try {
75
Class.forName("com.mysql.jdbc.D
river").newInstance();
} catch (Exception ex) {
// Tratar el error
}
Los controladores se identifican con un
URL JDBC de la forma
JDBC:subprotocolo:localizadorBD
El subprotocolo indica el tipo de BD
especifico.
El localizador permite referenciar de
forma única la BD
o Host y puerto(opcional)
o Nombre de la BD
La conexión de la BD se hace con el
método getConnection
public static Connection
getConnection(String url)
public static Connection
getConnection(String url, String user,
String password)
Ejemplo:
try {
conexion =
76
DriverManager.getConnection( "jdbc:
mysql://localhost/tienda","admin",
"1234");
} catch (SQLException ex) {
// Tratar el error
}
Statement
o Encapsula las instrucciones SQL a la BD
o Se crea a partir de la conexión
instruccion = conexion.createStatement();
o Métodos
executeQuery(String sql)
Ejecución de consultas: SELECT
Devuelve un objeto ResultSet
executeUpdate(String sql)
Modificaciones en la BD: INSERT,
UPDATE, DELETE
Devuelve el número de columnas
afectadas
execute(String sql)
Ejecución de instrucciones que pueden
devolver varios conjuntos de resultados
77
Requiere usar luego getResultSet() o
getUpdateCount() para recuperar los
resultados, y getMoreResults() para ver
los siguientes resultados
Ejemplo:
try {
ResultSet resultados =
instruccion.executeQuery(query);
while (resultados.next()) {
System.out.println("Cliente”
+resultados.getString("telefono") );
}
} catch (Exception ex) {
78
e.printStackTrace();
Liberación de Recursos
o Las interfaces ResultSet, PreparedStatement y
Connection disponen del método close.
o Cuando se cierra una conexión, se cierran todos
sus PreparedStatement asociados
o Cuando se cierra un PreparedStatement, se cierran
todos sus ResultSet asociados
o La implementación de la interfaz Connection debe
redefinir finalize para que invoque a close en caso
de que el desarrollador no lo haya hecho
NOTA: finalize es un método definido en
Object; el recolector de basura lo invoca antes
de eliminar un objeto de memoria
o En resumen, se crea la ilusión de que nunca es
necesario cerrar las conexiones explícitamente.
79