Clase Thread - docencia de la ETSIT-URJC
Clase Thread - docencia de la ETSIT-URJC
Clase Thread - docencia de la ETSIT-URJC
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
Tema 14: Concurrencia<br />
Introducción a Java<br />
GSyC, 2007
Introducción<br />
Las aplicaciones normalmente están compuestas <strong>de</strong> un<br />
único flujo <strong>de</strong> ejecución que ejecuta todas <strong>la</strong>s<br />
instrucciones <strong>de</strong>l programa<br />
Java proporciona soporte para <strong>la</strong> programación <strong>de</strong><br />
aplicaciones concurrentes: varios flujos <strong>de</strong> ejecución<br />
simultáneos en el mismo programa<br />
Un <strong>Thread</strong> es un flujo/hilo <strong>de</strong> ejecución, (tarea en<br />
Ada). Es una CPU virtual.<br />
En Java se pue<strong>de</strong>n crear múltiples threads, a<strong>de</strong>más<br />
<strong>de</strong>l flujo principal que da comienzo al programa,<br />
ejecutándose todos concurrentemente<br />
2
Introducción<br />
Un thread está compuesto por:<br />
• Código que ejecuta el thread<br />
• Datos que consulta/modifica el thread<br />
• Prioridad <strong>de</strong>l thread: utilizada para que el p<strong>la</strong>nificador <strong>de</strong>cida qué thread ejecutar<br />
cuando hay varios posibles<br />
Cómo se implementan los threads<br />
• Cuando se interrumpe <strong>la</strong> ejecución <strong>de</strong> un thread se salva el contador <strong>de</strong> programa, los<br />
registros y su pi<strong>la</strong> para po<strong>de</strong>r recuperar su estado cuando se continúe su ejecución<br />
• Pue<strong>de</strong> haber un thread por procesador en máquinas multiprocesadoras, o un thread<br />
por cada proceso <strong>de</strong>l sistema operativo (SO), o un thread por cada “proceso ligero”<br />
<strong>de</strong>l SO, o múltiples threads por cada proceso <strong>de</strong>l SO, contro<strong>la</strong>dos por un p<strong>la</strong>nificador<br />
que corre en espacio <strong>de</strong> usuario<br />
El soporte para threads lo pue<strong>de</strong> proporcionar el propio lenguaje, o una biblioteca aparte
Introducción<br />
Aspectos a tener en cuenta al añadir threads a un<br />
lenguaje <strong>de</strong> programación<br />
• Políticas <strong>de</strong> p<strong>la</strong>nificación <strong>de</strong> los threads<br />
• Cómo mo<strong>de</strong><strong>la</strong>r <strong>la</strong> periodicidad<br />
• Cómo implementar los threads: estructuras <strong>de</strong> control<br />
nuevas, o módulos especiales, o métodos especiales en<br />
módulos <strong>de</strong> <strong>la</strong> biblioteca<br />
• Cómo expresar el procesamiento que se llevará a cabo<br />
• Cómo expresar <strong>la</strong> semántica <strong>de</strong>l ciclo <strong>de</strong> vida <strong>de</strong> un thread:<br />
creación, arranque, parada<br />
• Cómo mo<strong>de</strong><strong>la</strong>r <strong>la</strong> sincronización y comunicación entre threads<br />
Cómo evitar condiciones <strong>de</strong> carrera: problemas que surgen al<br />
compartir estructuras <strong>de</strong> datos entre threads
<strong>Thread</strong>s en Java<br />
P<strong>la</strong>nificación <strong>de</strong> threads<br />
• Cada thread tiene su prioridad<br />
• Si hay más threads que procesadores en el or<strong>de</strong>nador, se elige el<br />
thread que se ejecutará según su prioridad<br />
2 posibilida<strong>de</strong>s, <strong>de</strong>pendientes <strong>de</strong> <strong>la</strong> implementación:<br />
Política expulsiva: cada cierto tiempo se le quita el procesador al<br />
thread que lo tiene<br />
Política no expulsiva: hasta que no se bloquea un thread no se le<br />
quita el procesador<br />
Ejecución periódica <strong>de</strong> threads<br />
• El método sleep (n) suspen<strong>de</strong> <strong>la</strong> ejecución <strong>de</strong>l thread durante al<br />
menos n milisegundos
<strong>Thread</strong>s en Java<br />
Java integra los threads con <strong>la</strong> orientación a objetos<br />
• Un thread es un objeto cuya c<strong>la</strong>se hereda <strong>de</strong> java.<strong>la</strong>ng.<strong>Thread</strong><br />
• El código que ejecutará el thread se escribe en su método<br />
run().<br />
• ¡El método run() NO se <strong>de</strong>be, aunque se pue<strong>de</strong>, invocar<br />
directamente!<br />
Ciclo <strong>de</strong> vida <strong>de</strong>l thread<br />
• Creación: Un thread es un objeto: se crea mediante su constructor<br />
• Arranque: El programador invoca el método start() <strong>de</strong>l thread<br />
para iniciar su ejecución. Es este método el que invocará el método<br />
run()<br />
• Terminación: Un thread termina su ejecución cuando su método<br />
run() ejecuta <strong>la</strong> sentencia return
Definición <strong>de</strong> threads<br />
En Java se pue<strong>de</strong> <strong>de</strong>finir y crear un<br />
thread <strong>de</strong> 2 formas:<br />
• Extendiendo <strong>la</strong> c<strong>la</strong>se <strong>Thread</strong><br />
• Se <strong>de</strong>c<strong>la</strong>ra una subc<strong>la</strong>se <strong>de</strong> <strong>Thread</strong> y se<br />
reemp<strong>la</strong>za su método run()<br />
• Luego se crea una instancia <strong>de</strong> <strong>la</strong> subc<strong>la</strong>se<br />
Implementando <strong>la</strong> interfaz Runnable<br />
• Se <strong>de</strong>c<strong>la</strong>ra una c<strong>la</strong>se que implementa <strong>la</strong><br />
interfaz Runnable, <strong>de</strong>finiendo el método<br />
run()<br />
• Luego se crea una instancia <strong>de</strong> esta c<strong>la</strong>se y se<br />
le pasa al constructor <strong>Thread</strong>()
Extendiendo <strong>la</strong> c<strong>la</strong>se <strong>Thread</strong><br />
public c<strong>la</strong>ss Nombre extends <strong>Thread</strong> {<br />
public Nombre ( String nombre ){<br />
super( nombre );<br />
}<br />
public void run() {<br />
while( true ) {<br />
System.out.println<br />
(System.currentTimeMillis() + ": " + getName());<br />
}<br />
}<br />
}<br />
public c<strong>la</strong>ss Prueba {<br />
public static void main( String[] args ) {<br />
Nombre n1 = new Nombre ( "Jaime" );<br />
Nombre n2 = new Nombre ( " Lo<strong>la</strong>" );<br />
n1.start();<br />
n2.start();<br />
}<br />
}<br />
<strong>Thread</strong> principal<br />
<strong>Thread</strong> Jaime<br />
<strong>Thread</strong> Lo<strong>la</strong><br />
$ java Prueba<br />
1177437609460: Jaime<br />
1177437609460: Lo<strong>la</strong><br />
1177437609461: Jaime<br />
1177437609461: Lo<strong>la</strong><br />
1177437609461: Jaime<br />
1177437609461: Lo<strong>la</strong><br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Jaime<br />
1177437609461: Lo<strong>la</strong><br />
1177437609461: Jaime
Cuidado con start() y run()!<br />
public c<strong>la</strong>ss Prueba {<br />
public static void main( String[] args ) {<br />
Nombre n1 = new Nombre ( "Jaime" );<br />
Nombre n2 = new Nombre ( " Lo<strong>la</strong>" );<br />
n1.run();<br />
n2.run();<br />
}<br />
}<br />
public c<strong>la</strong>ss Prueba {<br />
public static void main( String[] args ) {<br />
Nombre n1 = new Nombre ( "Jaime" );<br />
Nombre n2 = new Nombre ( " Lo<strong>la</strong>" );<br />
n1.start();<br />
n2.start();<br />
}<br />
}
Estados <strong>de</strong>l thread<br />
Cuando se crea un thread, éste no tiene aún <strong>la</strong> capacidad <strong>de</strong><br />
ejecución.<br />
Cuando se invoca al método start() el thread se encuentra en estado<br />
<strong>de</strong> “ejecutable” (“runnable”). Eso no significa que se ejecute!!!<br />
Cuando el p<strong>la</strong>nificador lo <strong>de</strong>cida, el thread pasa a un estado <strong>de</strong><br />
“ejecutando” (“running”).<br />
El thread pue<strong>de</strong> pasar a otro estado intermedio l<strong>la</strong>mado “bloqueado”<br />
(“blocked”) si realiza una operación <strong>de</strong> bloqueo (p.e. entrada/salida)<br />
Cuando sale <strong>de</strong>l estado <strong>de</strong> bloqueo, pasa <strong>de</strong> nuevo al estado <strong>de</strong><br />
“ejecutable”
Implementando <strong>la</strong> interfaz Runnable<br />
public c<strong>la</strong>ss Nombre implements Runnable {<br />
String nombre;<br />
public Nombre ( String nombre ){<br />
this.nombre = nombre;<br />
}<br />
public void run() {<br />
while( true ) {<br />
System.out.println<br />
(System.currentTimeMillis() + ": " + nombre);<br />
}<br />
}<br />
}<br />
public c<strong>la</strong>ss Prueba {<br />
public static void main( String[] args ) {<br />
Runnable n1 = new Nombre ( "Jaime" );<br />
Runnable n2 = new Nombre ( " Lo<strong>la</strong>" );<br />
<strong>Thread</strong> t1 = new <strong>Thread</strong>(n1); t1.start();<br />
<strong>Thread</strong> t2 = new <strong>Thread</strong>(n2); t2.start();<br />
}<br />
}<br />
<strong>Thread</strong> principal<br />
<strong>Thread</strong> Jaime<br />
<strong>Thread</strong> Lo<strong>la</strong><br />
$ java Prueba<br />
1177438860272: Lo<strong>la</strong><br />
1177438860272: Lo<strong>la</strong><br />
1177438860272: Jaime<br />
1177438860272: Lo<strong>la</strong><br />
1177438860272: Jaime<br />
1177438860272: Lo<strong>la</strong><br />
1177438860272: Jaime<br />
1177438860272: Lo<strong>la</strong><br />
1177438860272: Jaime<br />
1177438860272: Lo<strong>la</strong><br />
1177438860272: Jaime<br />
1177438860272: Jaime<br />
1177438860272: Jaime<br />
1177438860272: Jaime<br />
1177438860272: Jaime<br />
1177438860272: Jaime<br />
1177438860272: Lo<strong>la</strong><br />
1177438860272: Jaime<br />
1177438860272: Lo<strong>la</strong><br />
1177438860272: Jaime<br />
1177438860272: Lo<strong>la</strong><br />
1177438860272: Jaime
Implementando <strong>la</strong> interfaz Runnable<br />
¿Cuándo implementar <strong>la</strong> interfaz Runnable en lugar<br />
<strong>de</strong> exten<strong>de</strong>r <strong>la</strong> c<strong>la</strong>se <strong>Thread</strong>?<br />
• Cuando una c<strong>la</strong>se hereda <strong>de</strong> otra: no podría heredar también<br />
<strong>de</strong> <strong>Thread</strong><br />
• Cuando no queremos heredar todos los métodos <strong>de</strong> <strong>la</strong> c<strong>la</strong>se<br />
<strong>Thread</strong><br />
• Cuando queremos diseñar el programa sin tener en cuenta <strong>la</strong><br />
concurrencia, y posteriormente queremos añadir<br />
concurrencia<br />
A pesar <strong>de</strong> no heredar <strong>de</strong> <strong>Thread</strong> po<strong>de</strong>mos invocar<br />
métodos <strong>de</strong> instancia <strong>de</strong> <strong>la</strong> c<strong>la</strong>se <strong>Thread</strong>:<br />
• <strong>Thread</strong>.current<strong>Thread</strong>() <strong>de</strong>vuelve el thread actual<br />
Ej. <strong>Thread</strong>.current<strong>Thread</strong>().getName()
<strong>Thread</strong>.sleep()<br />
El método <strong>Thread</strong>.sleep() es un método estático<br />
<strong>de</strong> <strong>la</strong> c<strong>la</strong>se <strong>Thread</strong>: suspen<strong>de</strong> <strong>la</strong> ejecución <strong>de</strong>l thread<br />
que lo ejecuta durante el número <strong>de</strong> milisegundos<br />
especificado<br />
<strong>Thread</strong>.sleep () tiene una cláusu<strong>la</strong> throws<br />
InterruptedException, pero run() no, por lo que<br />
hay que tratar <strong>la</strong> excepción<br />
La ejecución <strong>de</strong>l bucle while no es muy precisa en<br />
cuanto al periodo
<strong>Thread</strong>.sleep()<br />
public c<strong>la</strong>ss Nombre extends <strong>Thread</strong> {<br />
int periodo;<br />
public Nombre ( String nombre, int periodo ){<br />
super( nombre );<br />
this.periodo = periodo;<br />
}<br />
public void run() {<br />
while( true ) {<br />
System.out.println<br />
(System.currentTimeMillis() + ": " + getName());<br />
try { <strong>Thread</strong>.sleep ( periodo ); }<br />
catch (InterruptedException e) {return ;}<br />
}<br />
}<br />
}<br />
public c<strong>la</strong>ss Prueba {<br />
public static void main( String[] args ) {<br />
Nombre n1 = new Nombre ( "Jaime”, 500 );<br />
Nombre n2 = new Nombre ( " Lo<strong>la</strong>”, 100 );<br />
n1.start();<br />
n2.start();<br />
}<br />
}<br />
<strong>Thread</strong> principal<br />
<strong>Thread</strong> Jaime<br />
<strong>Thread</strong> Lo<strong>la</strong><br />
$ java Prueba<br />
1177448963165: Jaime<br />
1177448963165: Lo<strong>la</strong><br />
1177448963265: Lo<strong>la</strong><br />
1177448963366: Lo<strong>la</strong><br />
1177448963466: Lo<strong>la</strong><br />
1177448963566: Lo<strong>la</strong><br />
1177448963665: Jaime<br />
1177448963667: Lo<strong>la</strong><br />
1177448963767: Lo<strong>la</strong><br />
1177448963867: Lo<strong>la</strong><br />
1177448963967: Lo<strong>la</strong><br />
1177448964068: Lo<strong>la</strong><br />
1177448964166: Jaime<br />
1177448964168: Lo<strong>la</strong><br />
1177448964268: Lo<strong>la</strong><br />
1177448964369: Lo<strong>la</strong><br />
1177448964469: Lo<strong>la</strong><br />
1177448964569: Lo<strong>la</strong>
Condiciones <strong>de</strong> carrera<br />
El siguiente programa tiene condiciones <strong>de</strong> carrera<br />
que no siempre se manifiestan cuando se ejecuta<br />
Los 2 threads que crea el programa principal<br />
comparten <strong>la</strong> misma instancia <strong>de</strong> <strong>la</strong> c<strong>la</strong>se Hucha<br />
El comportamiento <strong>de</strong>seable es que todas <strong>la</strong>s veces<br />
que se l<strong>la</strong>me al método mete() que<strong>de</strong>n reflejadas en<br />
el estado <strong>de</strong> <strong>la</strong> variable ahorros <strong>de</strong> <strong>la</strong> hucha<br />
• Sin embargo no siempre se obtiene este resultado, como se<br />
ve en <strong>la</strong>s ejecuciones <strong>de</strong>l programa
Ejemplo simple<br />
public c<strong>la</strong>ss Pi<strong>la</strong> {<br />
}<br />
int idx = 0;<br />
char [] data = new char[6];<br />
public void poner(char c) {<br />
}<br />
data[idx] = c;<br />
idx++;<br />
public char quitar() {<br />
}<br />
idx--;<br />
return data[idx];
Ejemplo<br />
El problema con el ejemplo anterior se reproduce<br />
cuando dos o más hilos acce<strong>de</strong>n a los métodos poner<br />
y quitar <strong>de</strong> forma <strong>de</strong>scontro<strong>la</strong>da.<br />
Debemos evitar que mientras un hilo intenta poner<br />
(quitar) el otro realice una operación que altere los<br />
datos.<br />
public c<strong>la</strong>ss Pi<strong>la</strong> {<br />
...<br />
...<br />
}<br />
public void poner(char c) {<br />
}<br />
synchronized (this) {<br />
data[idx] = c;<br />
idx++;<br />
}
public c<strong>la</strong>ss Hucha {<br />
private int ahorros = 0;<br />
public void mete ( int cantidad ) {<br />
ahorros = ahorros + cantidad;<br />
}<br />
public int ahorros () { return ahorros; }<br />
}<br />
public c<strong>la</strong>ss Ahorrador extends <strong>Thread</strong> {<br />
Hucha hucha;<br />
int iteraciones;<br />
public Ahorrador ( String nombre, Hucha hucha, int iteraciones ) {<br />
super (nombre);<br />
this.hucha = hucha;<br />
this.iteraciones = iteraciones;<br />
}<br />
public void run () {<br />
for (int i=1; i
$ java PruebaAhorradores 10<br />
Ahorros tras Pedro: 10<br />
Ahorros tras Juan : 20<br />
$ java PruebaAhorradores 100<br />
Ahorros tras Pedro: 100<br />
Ahorros tras Juan : 200<br />
$ java PruebaAhorradores 1000<br />
Ahorros tras Pedro: 1000<br />
Ahorros tras Juan : 2000<br />
$ java PruebaAhorradores 1000<br />
Ahorros tras Pedro: 1497<br />
Ahorros tras Juan : 1995<br />
$ java PruebaAhorradores 100000<br />
Ahorros tras Pedro: 100347<br />
Ahorros tras Juan : 184980<br />
$ java PruebaAhorradores 100000<br />
Ahorros tras Juan : 86727<br />
Ahorros tras Pedro: 171360<br />
$ java PruebaAhorradores 100000<br />
Ahorros tras Juan : 104166<br />
Ahorros tras Pedro: 188799<br />
Parece que funciona pero…<br />
Parece que funciona pero…<br />
Parece que funciona pero…<br />
… NO funciona: hay condiciones <strong>de</strong> carrera<br />
… NO funciona: hay condiciones <strong>de</strong> carrera<br />
… NO funciona: hay condiciones <strong>de</strong> carrera<br />
… NO funciona : hay condiciones <strong>de</strong> carrera
Condiciones <strong>de</strong> carrera<br />
¡ La sentencia <strong>de</strong> asignación NO se ejecuta <strong>de</strong> manera atómica !<br />
public c<strong>la</strong>ss Hucha {<br />
private int ahorros = 0;<br />
public void mete ( int cantidad ) {<br />
ahorros = ahorros + cantidad;<br />
}<br />
public int ahorros () { return ahorros; }<br />
}<br />
13 MOVE ahorros, A<br />
14 ADD A, cantidad<br />
15 MOVE A, ahorros<br />
<strong>Thread</strong> Pedro<br />
<strong>Thread</strong> Juan<br />
A cantidad PC ahorros<br />
0 1 13 0<br />
A cantidad PC ahorros<br />
0 1 13 0<br />
A cantidad PC ahorros<br />
1 1 14 0<br />
A cantidad PC ahorros<br />
1 1 15 1<br />
A cantidad PC ahorros<br />
0 1 13 1<br />
A cantidad PC ahorros<br />
1 1 14 1<br />
A cantidad PC ahorros<br />
1 1 15 1
Sincronización y comunicación <strong>de</strong> threads<br />
Los threads en Java se comunican y sincronizan l<strong>la</strong>mando a métodos<br />
marcados como synchronized, pertenecientes a otros objetos<br />
Un objeto con uno o más métodos synchronized tiene un lock<br />
interno (es un mutex, y sólo hay uno por objeto)<br />
• Si un thread l<strong>la</strong>ma a un método synchronized <strong>de</strong> un objeto que no<br />
está bloqueado, pue<strong>de</strong> continuar, bloqueándose atómicamente el objeto<br />
mientras dura <strong>la</strong> ejecución <strong>de</strong>l método synchronized<br />
• Si un thread l<strong>la</strong>ma a un método synchronized <strong>de</strong> un objeto<br />
bloqueado por otro thread, se suspen<strong>de</strong> <strong>la</strong> ejecución <strong>de</strong>l thread<br />
l<strong>la</strong>mante hasta que el objeto se <strong>de</strong>sbloquee
public c<strong>la</strong>ss Hucha {<br />
private int ahorros = 0;<br />
public synchronized void mete ( int cantidad ) {<br />
ahorros = ahorros + cantidad;<br />
}<br />
public synchronized int ahorros () { return ahorros; }<br />
}<br />
public c<strong>la</strong>ss Ahorrador extends <strong>Thread</strong> {<br />
Hucha hucha;<br />
int iteraciones;<br />
public Ahorrador ( String nombre, Hucha hucha, int iteraciones ) {<br />
super (nombre);<br />
this.hucha = hucha;<br />
this.iteraciones = iteraciones;<br />
}<br />
public void run () {<br />
for (int i=1; i
$ java PruebaAhorradores 10<br />
Ahorros tras Pedro: 10<br />
Ahorros tras Juan : 20<br />
$ java PruebaAhorradores 100<br />
Ahorros tras Pedro: 100<br />
Ahorros tras Juan : 200<br />
$ java PruebaAhorradores 1000<br />
Ahorros tras Pedro: 1292<br />
Ahorros tras Juan : 2000<br />
$ java PruebaAhorradores 1000<br />
Ahorros tras Pedro: 1499<br />
Ahorros tras Juan : 2000<br />
$ java PruebaAhorradores 100000<br />
Ahorros tras Pedro: 194544<br />
Ahorros tras Juan : 200000<br />
$ java PruebaAhorradores 100000<br />
Ahorros tras Juan : 191417<br />
Ahorros tras Pedro: 200000<br />
$ java PruebaAhorradores 100000<br />
Ahorros tras Juan : 197544<br />
Ahorros tras Pedro: 200000
Sincronización y comunicación <strong>de</strong> threads<br />
Mediante l<strong>la</strong>madas a wait(), notify() y notifyAll()<br />
<strong>de</strong>s<strong>de</strong> <strong>de</strong>ntro <strong>de</strong> métodos synchronized se pue<strong>de</strong> suspen<strong>de</strong>r o<br />
<strong>de</strong>suspen<strong>de</strong>r <strong>la</strong> ejecución <strong>de</strong> métodos en función <strong>de</strong>l estado <strong>de</strong>l<br />
objeto
public c<strong>la</strong>ss Hucha {<br />
private int ahorros = 0; private final int MAXIMO = 10;<br />
public synchronized void mete (int cantidad) {<br />
while (ahorros + cantidad > MAXIMO)<br />
try {wait();} catch (InterruptedException e) {return;};;<br />
ahorros = ahorros + cantidad;<br />
notifyAll(); // Despertamos a los gastadores, y al resto<br />
}<br />
public synchronized void saca (int cantidad) {<br />
while (ahorros < cantidad)<br />
try {wait();} catch (InterruptedException e) {return;};<br />
ahorros = ahorros - cantidad;<br />
notifyAll(); // Despertamos a los ahorradores, y al resto<br />
}<br />
public synchronized int ahorros () { return ahorros; }<br />
}<br />
public c<strong>la</strong>ss Gastador extends <strong>Thread</strong> {<br />
Hucha hucha; int iteraciones;<br />
public Gastador<br />
( String nombre, Hucha hucha, int iteraciones ) {<br />
super (nombre);<br />
this.hucha = hucha;<br />
this.iteraciones = iteraciones;<br />
}<br />
public void run () {<br />
for (int i=1; i
$ java PruebaAhorradores 1000 1000 2000<br />
Ahorros tras ahorrador 1: 7<br />
Ahorros tras ahorrador 2: 6<br />
Ahorros tras gastador 1 : 0<br />
$ java PruebaAhorradores 5 5 10<br />
Ahorros tras ahorrador 1: 5<br />
Ahorros tras gastador 1 : 0<br />
Ahorros tras ahorrador 2: 10<br />
$ java PruebaAhorradores 100 100 200<br />
Ahorros tras ahorrador 2: 0<br />
Ahorros tras gastador 1 : 0<br />
Ahorros tras ahorrador 1: 0<br />
$ java PruebaAhorradores 100 100 500<br />
Ahorros tras ahorrador 1: 4<br />
Ahorros tras ahorrador 2: 7<br />
^C<br />
$ java PruebaAhorradores 50 50 90<br />
Ahorros tras ahorrador 1: 5<br />
Ahorros tras gastador 1 : 1<br />
Ahorros tras ahorrador 2: 10<br />
$ java PruebaAhorradores 50 50 70<br />
Ahorros tras ahorrador 1: 10<br />
Ahorros tras gastador 1 : 10<br />
^C<br />
26
<strong>C<strong>la</strong>se</strong>s e interfaces en java.<strong>la</strong>ng<br />
Interfaz Runnable<br />
public interface Runnable {<br />
void run();<br />
}<br />
Métodos re<strong>la</strong>cionados con threads<br />
<strong>de</strong> <strong>la</strong> c<strong>la</strong>se Object<br />
public c<strong>la</strong>ss Object {<br />
public final void wait() throws InterruptedException;<br />
public final native void wait(long timeout)<br />
throws InterruptedException;<br />
public final void wait(long timeout, int nanos)<br />
throws InterruptedException;<br />
public final native void notify();<br />
public final native void notifyAll();<br />
}<br />
<strong>C<strong>la</strong>se</strong> <strong>Thread</strong><br />
public c<strong>la</strong>ss <strong>Thread</strong> implements Runnable {<br />
public final static int MIN_PRIORITY = 1;<br />
public final static int MAX_PRIORITY = 10;<br />
public final static int NORM_PRIORITY = 5;<br />
public <strong>Thread</strong>();<br />
public <strong>Thread</strong>(String name);<br />
public <strong>Thread</strong>(Runnable runObject);<br />
public <strong>Thread</strong>(Runnable runObject, String name);<br />
public String toString();<br />
public void run();<br />
public final void start()<br />
throws Illegal<strong>Thread</strong>StateException;<br />
public final void stop() throws SecurityException;<br />
public final void suspedn() throws SecurityException;<br />
public final void resume() throws SecurityException;<br />
public final String getName();<br />
public final int getPriority();<br />
public final int setPriority(int newPriority)<br />
throws SecurityException, IllegalArgumentException;<br />
public final void join() throws InterruptedException;<br />
public final void join(long millis)<br />
throws InterruptedException;<br />
public void interrupt();<br />
public boolean isInterrupted();<br />
}<br />
public static <strong>Thread</strong> current<strong>Thread</strong>();<br />
public static void yield();<br />
public static void sleep( long millis )<br />
throws InterruptedException;<br />
public void <strong>de</strong>stroy();
Otros<br />
Yield()<br />
• Avisa al scheduler que pue<strong>de</strong> interrumpir el thread. Es útil<br />
en algunos casos, pero no suele ser fiable.<br />
// Suggesting when to switch threads with yield().<br />
public c<strong>la</strong>ss Yielding<strong>Thread</strong> extends <strong>Thread</strong> {<br />
private static Test monitor = new Test();<br />
private int countDown = 5;<br />
private static int threadCount = 0;<br />
public Yielding<strong>Thread</strong>() {<br />
super("" + ++threadCount);<br />
start();<br />
}<br />
public String toString() {<br />
return "#" + getName() + ": " + countDown;<br />
}<br />
public void run() {<br />
while(true) {<br />
System.out.println(this);<br />
if(--countDown == 0) return;<br />
yield();<br />
}<br />
}<br />
public static void main(String[] args) {<br />
for(int i = 0; i < 5; i++)<br />
new Yielding<strong>Thread</strong>();<br />
}<br />
} //
Otros<br />
Join()<br />
• Hace que el thread espere a que otro thread haya<br />
terminado.<br />
// Un<strong>de</strong>rstanding join().<br />
c<strong>la</strong>ss Sleeper extends <strong>Thread</strong> {<br />
private int duration;<br />
public Sleeper(String name, int sleepTime) {<br />
super(name);<br />
duration = sleepTime;<br />
start();<br />
}<br />
public void run() {<br />
try {<br />
sleep(duration);<br />
} catch (InterruptedException e) {<br />
System.out.println(getName() + " was interrupted. " +<br />
"isInterrupted(): " + isInterrupted());<br />
return;<br />
}<br />
System.out.println(getName() + " has awakened");<br />
}<br />
}<br />
c<strong>la</strong>ss Joiner extends <strong>Thread</strong> {<br />
private Sleeper sleeper;<br />
public Joiner(String name, Sleeper sleeper) {<br />
super(name);<br />
this.sleeper = sleeper;<br />
start();<br />
}<br />
public void run() {<br />
try {<br />
sleeper.join();<br />
} catch (InterruptedException e) {<br />
throw new RuntimeException(e);<br />
}<br />
System.out.println(getName() + " join completed");<br />
}<br />
}
Join<br />
public c<strong>la</strong>ss Joining {<br />
private static Test monitor = new Test();<br />
public static void main(String[] args) {<br />
Sleeper<br />
sleepy = new Sleeper("Sleepy", 1500),<br />
grumpy = new Sleeper("Grumpy", 1500);<br />
Joiner<br />
dopey = new Joiner("Dopey", sleepy),<br />
doc = new Joiner("Doc", grumpy);<br />
grumpy.interrupt();<br />
}<br />
} /
Sectiones Críticas<br />
Un synchronized sobre un objeto:<br />
• Sirven para crear regiones críticas. Por ejemplo:<br />
synchronized(syncObject) {<br />
}<br />
// This co<strong>de</strong> can be accessed<br />
// by only one thread at a time
Wait and notify<br />
Se pue<strong>de</strong>n utilizar sobre un objeto sincronizado.<br />
c<strong>la</strong>ss Or<strong>de</strong>r {<br />
private static int i = 0;<br />
private int count = i++;<br />
public Or<strong>de</strong>r() {<br />
if(count == 10) {<br />
System.out.println("Out of food, closing");<br />
System.exit(0);<br />
}<br />
}<br />
public String toString() { return "Or<strong>de</strong>r " + count; }<br />
}<br />
c<strong>la</strong>ss WaitPerson extends <strong>Thread</strong> {<br />
private Restaurant restaurant;<br />
public WaitPerson(Restaurant r) {<br />
restaurant = r;<br />
start();<br />
}<br />
public void run() {<br />
while(true) {<br />
while(restaurant.or<strong>de</strong>r == null)<br />
synchronized(this) {<br />
try {<br />
wait();<br />
} catch(InterruptedException e) {<br />
throw new RuntimeException(e);<br />
}<br />
}<br />
System.out.println(<br />
"Waitperson got " + restaurant.or<strong>de</strong>r);<br />
restaurant.or<strong>de</strong>r = null;<br />
}<br />
}<br />
}<br />
c<strong>la</strong>ss Chef extends <strong>Thread</strong> {<br />
private Restaurant restaurant;<br />
private WaitPerson waitPerson;<br />
public Chef(Restaurant r, WaitPerson w) {<br />
restaurant = r;<br />
waitPerson = w;<br />
start();<br />
}<br />
public void run() {<br />
while(true) {<br />
if(restaurant.or<strong>de</strong>r == null) {<br />
restaurant.or<strong>de</strong>r = new Or<strong>de</strong>r();<br />
System.out.print("Or<strong>de</strong>r up! ");<br />
synchronized(waitPerson) {<br />
waitPerson.notify();<br />
}<br />
}<br />
try {<br />
sleep(100);<br />
} catch(InterruptedException e) {<br />
throw new RuntimeException(e);<br />
}<br />
}<br />
}<br />
}<br />
public c<strong>la</strong>ss Restaurant {<br />
private static Test monitor = new Test();<br />
Or<strong>de</strong>r or<strong>de</strong>r; // Package access<br />
public static void main(String args[]) {<br />
Restaurant restaurant = new Restaurant();<br />
WaitPerson waitPerson = new WaitPerson(restaurant);<br />
Chef chef = new Chef(restaurant, waitPerson);<br />
}<br />
} ///:~