39
Introdução à Programação Orientada a Objetos com Java Paulo Borba Centro de Informática Universidade Federal de Pernambuco Conceitos Básicos de Concorrência

Introdução à Programação Orientada a Objetos com Java

  • Upload
    alain

  • View
    33

  • Download
    2

Embed Size (px)

DESCRIPTION

Introdução à Programação Orientada a Objetos com Java. Conceitos Básicos de Concorrência. Paulo Borba Centro de Informática Universidade Federal de Pernambuco. Execução sequencial. Conta c; c = new Conta(“12-3”); c.creditar(100); c.debitar(80); System.out.print( c.getSaldo());. - PowerPoint PPT Presentation

Citation preview

Page 1: Introdução à Programação Orientada a Objetos com Java

Introdução à ProgramaçãoOrientada a Objetos com Java

Paulo BorbaCentro de Informática

Universidade Federal de Pernambuco

Conceitos Básicos de Concorrência

Page 2: Introdução à Programação Orientada a Objetos com Java

Execução sequencial

Conta c;c = new Conta(“12-3”);

c.creditar(100);c.debitar(80);

System.out.print( c.getSaldo());

Os comandos são

executados em

seqüência, um após o

outroO resultado da

execução é sempre o

mesmo: a conta tem $R 20,00

de saldo

Page 3: Introdução à Programação Orientada a Objetos com Java

Nas linguagens concorrentes...

A execução também pode ser concorrente:

dois ou mais comandos podem ser executados ao mesmo tempo

Na execução sequencial, só um comando é executado por vez

Page 4: Introdução à Programação Orientada a Objetos com Java

A execução concorrente é útil para... Permitir que vários

usuários utilizem os serviços de um dado sistema ao mesmo tempo

Aumentar a eficiência de um programa (mas cuidado!)

Fachada

GUI GUI

GUI

Page 5: Introdução à Programação Orientada a Objetos com Java

A execução concorrente é possível pois...Pode-se executar cada um dos comandos em

um processador diferente (paralelismo), ou

dividir o tempo da execução de um processador para executar os vários comandos, parte a parteOs comandos

normalmente não são atômicos

Page 6: Introdução à Programação Orientada a Objetos com Java

Os comandos e suas partes

double tmp = saldo;tmp = tmp + 5;saldo = tmp;

Até uma atribuição como

saldo = saldo + 5;

é executada por partes, similarmenteà execução da seqüência de comandosabaixo:

Page 7: Introdução à Programação Orientada a Objetos com Java

Execução concorrente

Conta c;c = new Conta(“12-3”);

c.creditar(100) || c.debitar(80)

System.out.print( c.getSaldo());

Os comandos podem ser

executados ao mesmo tempo

Pode-se executar

parte de um, parte de outro,

arbitrariamente,até completar a execução dos

dois

Page 8: Introdução à Programação Orientada a Objetos com Java

Mais execução concorrenteConta c;c = new Conta(“12-3”);

c.creditar(100) || c.debitar(80)

System.out.print( c.getSaldo());

Pode-se também executar o

primeiro por completo e

depois o segundo por completo, ou

vice-versa A escolha é

arbitrária, feitapelo interpretador

Page 9: Introdução à Programação Orientada a Objetos com Java

Concorrência e não determinismoConta c;c = new Conta(“12-3”);

c.creditar(100) || c.debitar(80)

System.out.print( c.getSaldo());

A execução pode gerar mais de um resultado!

Depende da ordem

escolhida para executar as partes dos comandos A escolha é

arbitrária, feitapelo interpretador

Page 10: Introdução à Programação Orientada a Objetos com Java

Conta: segura em um ambiente sequencial...

class Conta {... private double saldo = 0; void creditar(double vc) {1 double tmpc = saldo;2 tmpc = tmpc + vc;3 saldo = tmpc; } void debitar(double vd) {A double tmpd = saldo;B tmpd = tmpd - vd;C saldo = tmpd; }

Page 11: Introdução à Programação Orientada a Objetos com Java

Mas em um ambiente concorrente...

c.creditar(100) || c.debitar(80)

Várias seqüências de execução dos comandos são possíveis:

•1 2 3 A B C (resultado: 20)•A B C 1 2 3 (resultado: 20)•1 A 2 3 B C (resultado: -80)•A 1 2 B C 3 (resultado: 100)•...

Page 12: Introdução à Programação Orientada a Objetos com Java

Ambiente concorrente e interferências A execução de um comando pode

interferir na execução de um outro comando• As interferências ocorrem quando os

comandos compartilham (concorrem) pelos mesmos recursos (atributos, variáveis, etc.)

• Resultados não desejados podem ser gerados

Page 13: Introdução à Programação Orientada a Objetos com Java

Para evitar interferências indesejadas... Temos que controlar o acesso a

recursos compartilhados Temos que proibir a realização de

determinadas seqüências de execução

Temos que sincronizar as execuções:• uma espera pelo término da outra

Page 14: Introdução à Programação Orientada a Objetos com Java

Em Java, para sincronização, use synchronized Só um método synchronized pode

ser executado em um objeto por vez• Os outros ficam esperando!

Vários métodos não sincronizados podem ser executados, no mesmo objeto, ao mesmo tempo que um método synchronized

Page 15: Introdução à Programação Orientada a Objetos com Java

Conta: segura em um ambiente concorrente...class Conta {... private double saldo = 0; synchronized void creditar(double vc) {1 double tmpc = saldo;2 tmpc = tmpc + vc;3 saldo = tmpc; } synchronized void debitar(double vd) {A double tmpd = saldo;B tmpd = tmpd - vd;C saldo = tmpd; }

Page 16: Introdução à Programação Orientada a Objetos com Java

Conta: segura em um ambiente concorrente...

class Conta {... private double saldo = 0; private String numero; synchronized double getSaldo() { return saldo; } String getNumero() { return numero; }

Não precisaria sincronizar se fosse int: race condition

que não implicaria em interferência

Page 17: Introdução à Programação Orientada a Objetos com Java

Com sincronização, eliminamos interferências...c.creditar(100) || c.debitar(80)

Só algumas seqüências de execução são permitidas:

•1 2 3 A B C (resultado: 20)•A B C 1 2 3 (resultado: 20)

O programa é determinístico: só umresultado é possível

Page 18: Introdução à Programação Orientada a Objetos com Java

O comando synchronized de Java

synchronized void m(...) { corpo}

void m(...) { synchronized(this) { corpo }}

O modificador

EquivalentesO comando;

impede a execução

simultânea de trechos de

códigos sincronizados no

mesmo argumento

Page 19: Introdução à Programação Orientada a Objetos com Java

Conta: não segura em um ambiente concorrente...class Conta {... private double saldo = 0; void creditar(double vc) { double tmpc; synchronized(this) {tmpc =

saldo;} tmpc = tmpc + vc; synchronized(this) {saldo =

tmpc;} } Não há race

conditions, mas pode haver

interferências…

Page 20: Introdução à Programação Orientada a Objetos com Java

Notação alternativa para execução concorrenteConta c;c = new Conta(“12-3”);

fork{c.creditar(100);}; fork{c.debitar(80);};

System.out.print( c.getSaldo());

Não herda lock do pai…

Page 21: Introdução à Programação Orientada a Objetos com Java

Como solicitar a execução concorrente em Java? O operador de execução

concorrente ||, nem fork, não existe em Java!

Mas Java permite criar threads• Seqüências ou fluxos de execução que

podem ser executados concorrentemente

• Os comandos a serem executados são “empacotados” como threads

• Objetos da classe java.lang.Thread

Page 22: Introdução à Programação Orientada a Objetos com Java

A classe Thread de Java

Subclasses de Thread redefinem o método run

Os comandos a serem executados por um thread são os comandos do corpo do método run

Execução concorrente =

Criação e início de threads

Page 23: Introdução à Programação Orientada a Objetos com Java

c.creditar(1)||c.debitar(8) em JavaThread c = new Credito(...);Thread d = new Debito(...);c.start(); d.start();try { c.join(); d.join();} catch(InterruptedException e) {...}

Inicia a execuçãodo thread

Espera pelo término da execução do thread

Subclassesde Thread

Page 24: Introdução à Programação Orientada a Objetos com Java

Simulando o operador de execução concorrente

Thread a = new ThreadA(...);Thread b = new ThreadB(...);a.start(); b.start();try { a.join(); b.join();} catch(InterruptedException e) {...}

A || B

corresponde a

Page 25: Introdução à Programação Orientada a Objetos com Java

Um comando a ser executado concorrentemente...

class Credito extends Thread { private Conta conta; private double val; Credito(Conta c, double v) { conta = c; val = v; } public void run() { conta.creditar(val); }}

O comando

a ser executad

o ao mesmo tempo

que outro

Page 26: Introdução à Programação Orientada a Objetos com Java

E o outro comando...

class Debito extends Thread { private Conta conta; private double val; Creditar(Conta c, double v) { conta = c; val = v; } public void run() { conta.debitar(val); }}

Aqui poderiater vários comandos,

em seqüência

ou concorrente

stambém

Page 27: Introdução à Programação Orientada a Objetos com Java

Mas quando herança é um problema...

class Debito implements Runnable { private Conta conta; private double val; Creditar(Conta c, double v) { conta = c; val = v; } public void run() { conta.debitar(val); }}

Uma alternativa éimplementar a

interface Runnable...

Page 28: Introdução à Programação Orientada a Objetos com Java

c.creditar(1)||c.debitar(8) em Java, com Runnable

Thread c = new Credito(...);Thread d;d = new Thread(new Debito(...));c.start(); d.start();try { c.join(); d.join();} catch(InterruptedException e) {...}

Subclassede Thread

Implementação de Runnable

Page 29: Introdução à Programação Orientada a Objetos com Java

Sincronização com synchronized

A execução de um thread t tem que esperar pela execução de outro thread u quando• u está executando um método

sincronizado e t quer começar a executar um método sincronizado do mesmo objeto

Pode ser omesmo método

ou não

Page 30: Introdução à Programação Orientada a Objetos com Java

Sincronização com wait A execução de um thread t tem

que esperar pela execução de outro thread u quando• A semântica da aplicação requer que

uma operação de t só seja executada em condições que podem ser garantidas por uma operação de u

A aplicação solicita a espera

Page 31: Introdução à Programação Orientada a Objetos com Java

Impedindo débitos além do limiteclass Conta {... private double saldo = 0; synchronized void debitar(double v) { while (saldo < v){ wait(); } saldo = saldo - v; } ...

Interrompe a execuçãodo método e do threadassociado, que só volta

a executar quando notificado por outro

thread Assumindo que débitos

além do limite não geram erros, mas espera ...

Page 32: Introdução à Programação Orientada a Objetos com Java

Por que while e não if?

class Conta {... private double saldo = 0; synchronized void debitar(double v) { while (saldo < v){ wait(); } saldo = saldo - v; } ...

A notificação avisaque talvez seja

possível realizar o débito, mas não

garante! Temos que testar de novo...

Page 33: Introdução à Programação Orientada a Objetos com Java

Avisando que o saldo aumentou

class Conta {... synchronized void creditar(double v) { saldo = saldo + v; notifyAll(); }}

Avisa (notifica) a todos os threads que estavam

esperando pela execuçãode um método desse

(this) objeto

Page 34: Introdução à Programação Orientada a Objetos com Java

O método wait...

Só pode ser chamado dentro de um método synchronized

Coloca o thread executando o comando para esperar por uma notificação

Libera a restrição de sincronização • outro método sincronizado vai poder

começar a executar no mesmo objeto

Page 35: Introdução à Programação Orientada a Objetos com Java

Os métodos notify e notifyAll O método notifyAll acorda todos

os threads esperando para executar um método do objeto que executou o notifyAll

O método notify só acorda um dos processos• a escolha é arbitrária, feita pelo

interpretador

Page 36: Introdução à Programação Orientada a Objetos com Java

Cuidado com concorrência! Os mecanismos para sincronização

diminuem a eficiência do sistema e podem complicar o código

O sistema pode entrar em deadlock!• Todos os threads parados, um esperando

pelo outro O sistema pode entrar em livelock!

• Threads executando mas não gerando nada que possa ser observado pelo usuário

Page 37: Introdução à Programação Orientada a Objetos com Java

Modelos alternativos para concorrência Escalonamento não pré-emptivo

• a transferência de controle ocorre somente em pontos bem definidos da execução de uma tarefa

Escalonamento pré-emptivo • uma transferência de controle entre tarefas pode

ocorrer a qualquer momento Troca de mensagens

Page 38: Introdução à Programação Orientada a Objetos com Java

Indo além...

Terminação de threads pais e filhos• daemons

Prioridades em threads Thread do AWT Concorrência e arquitetura em

camadas:• BD pode eliminar a necessidade de

sincronizar classes básicas

Page 39: Introdução à Programação Orientada a Objetos com Java

Claus Reinke [email protected] at [email protected]

As you are aware, simply assigning to channel in one thread doesn'tbring the value into main memory, and simply reading in the otherdoesn't ensure that what we are reading is consistent with mainmemory. So Joe R. Hacker has searched the JLS for a solution (which,on some fine Friday 13th, you might find in your codebase).According to JLS, *all* pending writes are committed to main memorybefore releasing *any* lock, and *all* first reads after acquiring*any* lock have to be preceded by a lookup in main memory.  So, byusing some dummy synch after writing and before reading, the codeensures that channel data travels from thread to thread, via mainmemory. Removing the dummy synch increases the likelihood of thetests to fail, as the code no longer explicitly ensuressynchronisation, but implementations are free to add any number ofextra synchs, so the tests might still go through.

class PlayingField {  static int channel;  // public PlayingField() { channel = 0; }  public static void init() { channel = 0; }    static void dummy() {      // synchronized (new Object()) { }         //1      // System.out.println("hithere");          //2      // System.out.println("hi"+"there");       //3      new StringBuffer("hi").append("there"); //4      // new StringBuffer("hithere");            //5      // new String("hithere");                  //6      // new Object();                           //7    }  static public void codeA(int no) {      int count=0,got;      channel = no; /* */ dummy();      got     = channel;      while (got==no) { /* */ dummy(); got = channel; count++; }      // channel = 0; /* */ dummy();      if (got==-no)         System.err.println("A ("+no+")  ["+count+"] done ");      else        System.err.println("A ("+no+")  ["+count+"] failed ");    }  static public void codeB(int no) {      int count=0,got;      got = channel;      while (got<=0) { /* */ dummy(); got = channel; count++; }      channel = -no; /* */ dummy();      if (got==no)         System.err.println("B ("+no+")  ["+count+"] done ");      else        System.err.println("B ("+no+")  ["+count+"] failed ");    }}

// ready? okay, now let's put that code through a few tests;// I do assume you've kept the original for comparison?-)public class Tst {  static Object lock = new Object();  static int no=0;  // static PlayingField playingField;  static volatile boolean ready=false,readyA=false,readyB=false;  static void tst() {    synchronized (lock) { no++;      System.err.println("\ntst ["+no+"]");      // playingField = new PlayingField();      PlayingField.init();      ready = false; readyA = false; readyB = false;    }    Thread A = new Thread() {                    public void run() {                      int no=0;                      synchronized (lock) { no=Tst.no; readyA=true; }                      while (!ready) {};                      PlayingField.codeA(no);                    }                 };    Thread B = new Thread() {                    public void run() {                      int no=0;                      synchronized (lock) { no=Tst.no; readyB=true; }                      while (!ready) {};                      PlayingField.codeB(no);                    }                 };    B.start();    A.start();    while (!(readyA && readyB)) {}    ready = true;    for(int i=0;i<100000000;i++) {}    // try { Thread.sleep(1000); } catch (InterruptedException e) {}    // try { A.join(); } catch (InterruptedException e) {}    // try { B.join(); } catch (InterruptedException e) {}  }  public static void main(String[] args) {     for (int i=0; i<100; i++) { tst(); }   }}