⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 java15.htm

📁 E-books about Java Programing in Spanish
💻 HTM
字号:
<HTML>

<HEAD>

<TITLE>Java desde Cero</TITLE>



<META NAME="GENERATOR" CONTENT="Internet Assistant for Microsoft Word 2.0z">

</HEAD>

<BODY background=/iconos/1.gif  TEXT=000000 LINK=FF0000 VLINK=A62A2A>
<H1>La liebre y la tortuga (y el guepardo)<BR>

</H1>

<P>

Java dispone de un mecanismo de prioridades para los <I>threads</I>,

de modo de poder asignar m&aacute;s tiempo de CPU a un thread

que a otro. T&iacute;picamente se asigna una prioridad de 1 a

10 (10 es la mayor prioridad) mediante <I>setPriority</I>, como

en el ejemplo que sigue:<BR>

<PRE>

<FONT SIZE=2>public class Ejemplo21 {



    static Animal	tortuga;

    static Animal	liebre;

    static Animal	guepardo;



    public static void main(String argv[])

      throws InterruptedException {



	tortuga  = new Animal(2, &quot;T&quot;);

	liebre   = new Animal(3, &quot;L&quot;);

	guepardo = new Animal(4, &quot;G&quot;);

	tortuga.start();

	liebre.start();

	guepardo.start();

	tortuga.join();

	liebre.join();

	guepardo.join();

    }

}



class Animal extends Thread {

	String nombre;



	public Animal(int prioridad, String nombre) {

		this.nombre = nombre;

		setPriority(prioridad);

	}



	public void run() {

		for (int x = 0; x &lt; 30; x++) {

			System.out.print( nombre );

			yield();

		}

		System.out.println(&quot;\nLlega &quot;+nombre );

	}

}

</FONT>

</PRE>

<P>

La salida de este programa, ejecutado con <I>java Ejemplo21</I>,

es por ejemplo:<BR>

<PRE>

<FONT SIZE=2>C:\java\curso&gt;java Ejemplo21

GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG

Llega G

L<B>T</B>LLLLLLLLLLLLLLLLLLLLLLLLLLLLL

Llega L

TTTTTTTTTTTTTTTTTTTTTTTTTTTTT

Llega T

</FONT>

</PRE>

<P>

Como se ve, a pesar de haber arrancado antes la tortuga, casi

todo el tiempo de CPU lo usa primero el Guepardo, luego la Liebre

(aunque algo queda para la pobre tortuga, como se ve en la T marcada),

y finalmente para la Tortuga. No todas las corridas ni todos los

sistemas dan igual salida, ya que &eacute;sta depende de la carga

del procesador y de la implementaci&oacute;n de Java particular.

<P>

Este programa simplemente crea tres animales (clase <FONT FACE="Arial">Animal</FONT>),

asigna un thread a cada uno y los ejecuta. Este ejemplo est&aacute;

hecho en base a uno del libro &quot;Programaci&oacute;n Java&quot;

de Macary y Nicolas.

<H2>Sincronicemos los relojes</H2>

<P>

Un problema b&aacute;sico del multithreading es cuando varios

programas (o, para el caso, varios threads) acceden a los mismos

datos: &#191;c&oacute;mo sabemos si uno de ellos no los modifica

mientras los est&aacute; usando otro?.

<P>

Veamos un ejemplo, donde suponemos que varios threads usan la

variable <FONT FACE="Arial">valorImportante</FONT>:<BR>

<PRE>

<FONT SIZE=2>	if  (valorImportante &gt; 0 ) {

		..... algo se procesa ac&aacute; ........

<B>		valorImportante = valorImportante - 1;

</B>		..... sigue.....................

	}<BR>

</FONT>

</PRE>

<P>

&#191;C&oacute;mo nos aseguramos que <FONT FACE="Arial">valorImportante</FONT>

no cambi&oacute; entre el <FONT FACE="Arial">if</FONT> y la l&iacute;nea

resaltada? Otros threads pueden haberlo modificado mientras tanto.

Asimismo, puede suceder que dos threads est&eacute;n ejecutando

la misma porci&oacute;n de c&oacute;digo, y se pierda uno de los

decrementos. Imaginen algo as&iacute;:

<PRE>

<FONT SIZE=2>	(antes)		valorImportante = 10

	(thread 1)	lee valorImportante = 10

	(thread 2)	lee valorImportante = 10

	(thread 1)	10 -1 = 9

	(thread 2)	10 -1 = 9

	(thread 2)	asigna 9 a valorImportante

	(thread 1)	asigna 9 a valorImportante

	(despu&eacute;s)	valorImportante = 9</FONT>

</PRE>

<P>

Como vemos, a pesar de haber restado dos veces, hemos perdido

una de las restas. Aunque usemos -= en vez de la resta es lo mismo,

porque el c&oacute;digo igualmente se resuelve en varios pasos

(varias <I>operaciones at&oacute;micas</I>).

<P>

Para evitar esto, Java nos brinda la palabra clave Synchronized,

que bloquea el acceso a una variable a todos los threads menos

el que lo est&aacute; usando.

<P>

Vamos a ver un caso espec&iacute;fico; se trata de dos contadores

que usan el mismo sumador para sumar de a uno una cantidad <FONT FACE="Arial">a</FONT>.

Supuestamente entre los dos deben llevar el sumador (<FONT FACE="Arial">a</FONT>)

hasta 20000.<BR>

<PRE>

<FONT SIZE=2>// Archivo Ejemplo22.java, compilar con javac Ejemplo22.java, ejecutar con java Ejemplo22



public class Ejemplo22 {

	public static void main(String argv[]) {

		Sumador A = new Sumador();		// un &uacute;nico sumador

		Contador C1 = new Contador(A);	// dos threads que lo usan...

		Contador C2 = new Contador(A);	// ...para sumar

		C1.start();

		C2.start();

		try {

			C1.join();

			C2.join();

		}

		catch (Exception e) {

			System.out.println(e);

		}

	}

}





class Contador extends Thread {

	Sumador s;



	Contador (Sumador sumador) {

		s = sumador;		// le asigno un sumador a usar

	}



	public void run() {

		s.sumar();		// ejecuto la suma

	}

}





class Sumador {

	int a = 0;

	public void sumar() {

		for (int i=0; i&lt;10000; i++ ) {

			if ( (i % 5000) == 0 ) {		// &quot;%&quot; da el resto de la divisi&oacute;n:

				System.out.println(a);	// imprimo cada 5000

			}

			a += 1;

		}

		System.out.println(a);			// imprimo el final

	}

}



</FONT>

</PRE>

<P>

Ejecutando esto nos da m&aacute;s o menos as&iacute; (cada corrida

es diferente, dependiendo de c&oacute;mo se &quot;chocan&quot;

los threads y la carga de la CPU):<BR>

<PRE>

<FONT SIZE=2>C:\java\curso&gt;java Ejemplo22

0

87

8926

10434

14159

17855<BR>

</FONT>

</PRE>

<P>

Esto se debe justamente a lo que explic&aacute;bamos al principio:

a veces los dos threads intentan ejecutar <FONT FACE="Arial">a

+= 1</FONT> simult&aacute;neamente, con lo que algunos incrementos

se pierden.

<P>

Podemos solucionar esto modificando el m&eacute;todo <FONT FACE="Arial">run()</FONT>:

<BR>

<PRE>

<FONT SIZE=2>	public void run() {

<B>		synchronized (s) {

</B>			s.sumar();

<B>		}

</B>	}<BR>

</FONT>

</PRE>

<P>

Con esto, s&oacute;lo a uno de los dos threads se les permite

ejecutar s.sumar() por vez, y se evita el problema. Por supuesto,

el otro thread queda esperando, por lo que m&aacute;s vale no

utilizar esto con m&eacute;todos muy largos ya que el programa

se puede poner lento o a&uacute;n bloquearse.

<P>

La salida ahora ser&aacute;:<BR>

<PRE>

<FONT SIZE=2>C:\java\curso&gt;java Ejemplo22

0				&lt;

5000				&lt; primer thread

10000				&lt;

10000				(

15000				( segundo thread

20000				(<BR>

</FONT>

</PRE>

<P>

Lo mismo logramos (y en forma m&aacute;s correcta) declarando

como synchronized al m&eacute;todo <FONT FACE="Arial">sumar()</FONT>:

<PRE>

<FONT SIZE=2>	public synchronized void sumar() { .............</FONT>

</PRE>

<P>

Esto es mejor porque la clase que llama a <FONT FACE="Arial">sumar()</FONT>

no necesita saber que tiene que sincronizar el objeto antes de

llamar al m&eacute;todo, y si otros objetos (en otros threads)

lo llaman, no necesitamos preocuparnos.<BR>

<H2>M&aacute;s sincronizaci&oacute;n</H2>

<P>

Otra manera de sincronizar el acceso de los threads a los m&eacute;todos,

es lograr que &eacute;stos se pongan de acuerdo entre s&iacute;,

esperando uno hasta que otro realiz&oacute; alguna tarea dada.

Para esto se usan los m&eacute;todos <FONT FACE="Arial">wait()</FONT>

y <FONT FACE="Arial">notify()</FONT>. Cuando un thread llama a

<FONT FACE="Arial">wait()</FONT> en un m&eacute;todo de un objeto

dado, queda detenido hasta que otro thread llame a <FONT FACE="Arial">notify()</FONT>

en alg&uacute;n m&eacute;todo del mismo objeto.

<P>

Por ejemplo, vamos a suponer cuatro empleados que se encuentran

con su jefe y lo saludan, pero s&oacute;lo luego de que &eacute;ste

los salude primero.<BR>

<PRE>

<FONT SIZE=2>public class Ejemplo23 {



	public static void main(String argv[]) {

		Saludo hola = new Saludo();

		Personal pablo = new Personal(hola, &quot;Pablo&quot;, false);

		Personal luis = new Personal(hola, &quot;Luis&quot;, false);

		Personal andrea = new Personal(hola, &quot;Andrea&quot;, false);

		Personal pedro = new Personal(hola, &quot;Pedro&quot;, false);

		Personal jefe = new Personal(hola, &quot;JEFE&quot;, true);

		pablo.start();

		luis.start();

		andrea.start();

		pedro.start();

		jefe.start();

		try {

			pablo.join();

			luis.join();

			andrea.join();

			pedro.join();

			jefe.join();

		}

		catch (Exception e) {

			System.out.println(e);

		}

	}

}





class Saludo {

	synchronized void esperarJefe(String empleado) {

	  try {

		wait();

		System.out.println(empleado+&quot;&gt; Buenos dias jefe!&quot;);

	  }

	  catch (InterruptedException e) {

		System.out.println(e.toString());

		

	  }

	}



	synchronized void saludoJefe() {

		System.out.println(&quot;JEFE&gt; Buenos dias!&quot;);

		notifyAll();

	}

}





class Personal extends Thread {

	String nombre;

	Saludo saludo;

	boolean esJefe;



	Personal (Saludo s, String n, boolean j) {

		nombre = n;

		saludo = s;

		esJefe = j;

	}



	public void run() {

		System.out.println(&quot;(&quot;+nombre+&quot; llega)&quot;);

		if (esJefe)

			saludo.saludoJefe();

		else

			saludo.esperarJefe(nombre);

	}

}<BR>

</FONT>

</PRE>

<P>

Us&eacute; <FONT FACE="Arial">notifyAll()</FONT> en lugar de <FONT FACE="Arial">notify()</FONT>,

porque en el segundo caso s&oacute;lo se notificar&iacute;a al

primer thread (el primer empleado en llegar) y no a los dem&aacute;s,

que se quedar&iacute;an en el <FONT FACE="Arial">wait()</FONT>.

<P>

Como se ve en la salida, a pesar de que los empleados est&aacute;n

en condiciones de saludar, no lo hacen hasta que no llega el jefe:

<BR>

<PRE>

<FONT SIZE=2>C:\java\curso&gt;java Ejemplo23

(Pablo llega)

(Luis llega)

(Andrea llega)

(Pedro llega)

(JEFE llega)

JEFE&gt; Buenos dias!

Luis&gt; Buenos dias jefe!

Pedro&gt; Buenos dias jefe!

Andrea&gt; Buenos dias jefe!

Pablo&gt; Buenos dias jefe!<BR>

</FONT>

</PRE>

<P>

Aqu&iacute; hice trampa: a veces, el jefe llega y saluda antes

que alguno de los empleados, por lo que ese empleado se queda

esperando indefinidamente. Prueben de modificar las clases para

que el jefe no salude hasta que no est&eacute;n todos los empleados

presentes...

<P>

Un buen ejercicio antes del pr&oacute;ximo cap&iacute;tulo!<BR>

<BR>

<P>

Jorge Bourdette

<P>

<A HREF="file:///C:/WINDOWS/Personal/mailto:jpb@amarillas.com" >jpb@amarillas.com</A>

<BR>

</BODY>

</HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -