Concurrencia en Android LSUB, GYSC, URJC
Repaso de concurrencia en Java
Crear un thread Instanciar un Thread, con el método run sobreescrito Intanciar un objeto que cumpla el interfaz Runnable y pasárselo al constructor de Thread! interface Runnable { ; void run();
Ejecutar un Thread Llamar al método start El método principal del hilo se llama run
Implementando Runnable! public class Biteme implements Runnable {! @Override public void run() { //Ejecuta en paralelo! Puedo crear todos los threads que quiera t = new Thread(new Biteme()); t.start(); b = new Thread(new Biteme()); b.start();
Heredando de Thread! public class Biteme extends Thread {! @Override public void run() { //Ejecuta en paralelo! t = new Biteme(); t.start(); b = new Biteme(); b.start(); Y se crean
Heredando de Thread clase interna! public class Cup { public int level; private class Biteme extends Thread { @Override public void run() { //Ejecuta en paralelo, tiene acceso a Cup level = 3; public void fill() { Biteme b = new Biteme(); b.start();
Heredando de Thread clase interna anónima public class Cup { public int level; public void fill() { Thread b = new Thread(){ @Override public void run() { level = 3; ; b.start();
Thread Thread, metodos de clase interesantes: currentthread(): referencia al thread actual activecount(): numero de threads activos dumpstack(): pila en stderr sleep(int ms) yield(): cede el cuanto
Executor java.util.concurrent Interfaz para ejecutar objetos de tipo Runnable Puede hacerlo en serie, en paralelo, con unos cuantos threads, etc.! interface Executor { public void execute(runnable r); ;
Executor: ejemplo de uso! SerialExecutor s = new SerialExecutor(); BiteMe b = new BiteMe(); //Runnable s.execute(b); s.execute(b);
ExecutorService Es un interfaz que hereda de Executor Con extras para controlar el progreso Future<?> submit(runnable task) Future tiene un método get() que devuelve null si ha acabado y otra cosa si no Future tiene más métodos para controlar el progreso y cancelar
ExecutorService!! ExecutorService es = Executors.newFixedThreadPool(3); BiteMe b = new BiteMe(); //Runnable BiteMe bx = new BiteMe(); //Runnable Future<?> fut1 = es.submit(b); Future<?> fut2 = es.submit(bx); try{ if(fut1.get() == null){ System.out.println("b ha acabado bien"); if(fut2.get() == null){ System.out.println("bx ha acabado bien"); catch(executionexception e){ //ha fallado la ejecucion catch(interruptedexception e){ //se ha interrumpido finally{ es.shutdown(); //acabo con el executor service
Interrupciones Son mala idea como método de comunicación Mejor comunicarse con variables y que el thread salga o haga lo que sea el mismo Pueden ser necesarios si tengo un thread en sleep Recoger InterruptedException
Condiciones de carrera Si tengo varios hilos, puedo tener condiciones de carrera Ojo con tocar datos compartidos sin protección!!!
Sincronización Puedo esperar que un thread acabe llamando a su método join() puedo poner timeout! t.start(); t.join(); //me quedo bloqueado hasta que acabe
Sincronización Puedo esperar que todos los threads de un executor service acaben, tiene timeout: awaittermination() Antes tengo que haber llamado a shutdown(), ya no admitas más threads, ve saliendo! e.awaittermination(3, TimeUnit.SECONDS);
Sincronización: synchronized Hay un cierre reentrante asociado a los objetos (un objeto es un monitor) Se coge (se entra al monitor) llamando a un método synchronized o rodeando un bloque con synchronized
Sincronización: synchronized public class Cup { public synchronized void fill() { //Estoy en el monitor de esta instancia de Cup public void fill2() { BiteMe b = new BiteMe(); synchronized(b){ //Estoy en el monitor b
Sincronización: synchronized public class Cup { public synchronized void fill() { //Estoy en el monitor de esta instancia de Cup public void fill2() { synchronized(this){ //Estoy en el monitor de esta instancia de Cup
Sincronización: synchronized Ojo, los métodos estáticos cogen el cierre de la clase public class Cup { public static synchronized void fill3() { //Estoy en el monitor de la clase Cup
Sincronización: synchronized public class Cup { public synchronized void fill() { //Estoy en el monitor de esta instancia de Cup public synchronized void fill2() { fill(); //No hay deadlock, es un monitor
Sincronización: comunicación BlockingQueue: FIFOs bloqueantes, como canales ConcurrentMap: Diccionario (nombrevalor) con operaciones atómicas
Sincronización Decorador sincronizado para colecciones (hay otros especializados): public static <T> Collection<T> synchronizedcollection(collection<t> c) Ojo, hay que sincronizar el acceso! Collection c = Collections.synchronizedCollection(myCollection);... synchronized(c) { Iterator i = c.iterator(); // Ojo, dentro de un bloque synchronized while (i.hasnext()) foo(i.next());
Concurrencia en Android
Problemas GUI no responde: las callbacks no pueden estar mucho rato bloqueadas; usar threads Threads en background no pueden modificar la GUI
Alternativas Crear threads, esperar a que acaben, actualizar la GUI, join() o awaittermination() Usar View.post para actualizar la GUI Usar AsyncTask, divide tareas entre threads en background y threads de GUI No actualizo la GUI si no estoy en el hilo principal!!!
View.post Tengo un hilo que he creado corriendo en background Quiero que cambie la UI Por ejemplo una barra de progreso Uso el método post para pasarle un Runnable que ejecutará el hilo asociado al View
Ejemplo: Botón para un tono: public class Tono implements OnClickListener { View but; void playsound(){ //suena Tono(View v) { but = v; //inicializo el tono private class Pressme implements Runnable { boolean p; View v; Pressme(View b, boolean pressed){ p = pressed; v = b; @Override public void run() { v.setenabled(!p);
Ejemplo: Botón para un tono:! @Override public void onclick(view arg0) { arg0.setenabled(false); Thread t = new Thread(){ public void run(){ playsound(); try{ Thread.sleep(3*1000); catch(interruptedexception e){ but.post(new Pressme(but, false)); ; t.start();
Ejemplo: OJO El hilo tiene una referencia al botón Que vive en la Activity Qué sucede si la Activity se recrea? Por ejemplo, si giran la pantalla OJO: las Activities son volátiles!!!
AsyncTask Crea un hilo cuando se llama al método execute() Ejecuta algunos métodos en el contexto de ese hilo Y otros en el contexto de la GUI (son callbacks), en particular de una View
AsyncTask private class SomeTask extends AsyncTask<Type1, Type2, Type3> { public Type3 doinbackground(type1... params) { return(dononuistuffwith(params)); public void onpostexecute(type2 result) { douistuff(result); new SomeTask().execute(type1VarA, type1varb);!
AsyncTask Los tres parámetros del tipo genérico: AsyncTask<Type1, Type2, Type3> Type1 Parámetros de doinbackground(type1...) Type 2 registro de progreso onprogressupdate(type2...) Type 3 resultado de Type3 doinbackground(type1...)
AsyncTask doinbackground() ejecuta en un hilo en background onpreexecute() onpostexecute() ejecutan en GUI antes y después del hilo onprogressupdate() lo ejecuta el GUI, cuando se ha llamado a publishprogress() El argumento de onprogressupdate() es lo que sea que se le pasó a publishprogress()