É possível que várias tarefas no sistema precisem acessar e manipular o mesmo objeto. O ILock define o protocolo para conceder acesso exclusivo a um objeto compartilhado. Quando uma tarefa precisa acessar o objeto compartilhado, elaadquire uma trava para esse objeto. Quando a manipulação do objeto é concluída, ela libera a trava.
Uma trava é normalmente criada quando o objeto compartilhado é criado ou acessado pela primeira vez por um plug-in. Ou seja, o código que tem uma referência ao objeto compartilhado também tem uma referência para sua trava. Iniciaremos ao criar uma trava, myLock, que será utilizado para controlar o acesso ao myObject:
... myObject = initializeImportantObject(); IJobManager jobMan = Platform.getJobManager(); myLock = jobMan.newLock(); ...
Uma implementação robusta de ILock é fornecida pela plataforma. O gerenciador de tarefa fornece instâncias dessa trava para utilização pelos clientes. Essas travas estão cientes umas das outras e podem evitar conflito circular.(Explicaremos mais sobre essa instrução em breve.)
Sempre que um código em uma tarefa requer acesso a myObject, primeiramente, deve adquirir a trava. O seguinte fragmento mostra um idioma comum para trabalhar com uma trava:
... // Preciso manipular myObject, então obtenho a trava primeiramente. try { myLock.acquire(); updateState(myObject); // manipulate the object } finally { lock.release(); } ...
O método acquire() não será executado até que a tarefa chamada possa ser receber acesso exclusivo à trava. Em outras palavras, se alguma outra tarefa já tiver adquirido a trava, esse código será bloqueado até que a trava esteja disponível. Observe que o código que adquire a trava e manipula myObject está agrupado em uma trava try para que a trava possa ser liberada se exceções ocorram ao trabalhar com o objeto.
Parece simples o suficiente, certo? Felizmente, as travas são muito simples de utilizar. Elas também são reutilizáveis, isso significa que você não tem que se preocupar com a sua tarefa e adquirir a mesma trava várias vezes. Cada trava mantém uma contagem do número de aquisições e liberações de um encadeamento específico e será liberada somente a partir de uma tarefa quando o número de liberações for igual ao número de aquisições.
Anteriormente observamos que as travas fornecidas pelo gerenciador de tarefa estão cientes umas das outras e podem evitar conflito circular. Para entender como o conflito ocorre, observaremos um cenário simples. Suponha que a "Tarefa A" adquira a "Trava A" e subseqüentemente tente adquirir a "Trava B." Enquanto isso, a "Trava B" é mantida pela "Tarefa B" que, agora, está bloqueada, aguardando na "Trava A." Esse tipo de conflito indica um problema de design de suporte com a utilização das travas entre as tarefas. Enquanto esse caso simples pode ser evitado facilmente, as chances de acidentalmente introduzir um conflito aumenta na medida em que o número de tarefas e travas utilizadas no design aumenta.
Felizmente, a plataforma o ajudará a identificar conflitos. Quando o gerenciador de tarefa detectar uma condição de conflito, imprime as informações de diagnóstico para o log, descrevendo a condição do conflito. O conflito é quebrado, concedendo temporariamente o acesso às travas de propriedade de uma tarefa bloqueada às outras tarefas que estão aguardando. É importante testar cuidadosamente qualquer implementação envolvendo várias travas e corrigir as condições de conflito que são relatadas pela plataforma.