En av de största utmaningarna med en komplext system är att uppehålla tillgängligheten medan uppgifter utförs. Utmaningen blir ännu större i ett utökningsbart system, när komponenter som inte har avsetts att köras tillsammans ändå delar samma resurser. Det här problemet hanteras med paketet org.eclipse.core.runtime.jobs genom en infrastruktur för schemaläggning, körning och hantering av operationer som körs samtidigt. Infrastrukturen baseras på användningen av jobb som representerar en arbetsenhet som kan köras asynkront.
class TrivialJob extends Job { public TrivialJob() { super("Trivial Job"); } public IStatus run(IProgressMonitor monitor) { System.out.println("This is a job"); return Status.OK_STATUS; } }Jobbet skapas och schemaläggs med följande kodstycke:
TrivialJob job = new TrivialJob(); System.out.println("About to schedule a job"); job.schedule(); System.out.println("Finished scheduling a job");Utdata för det här programmet är beroende av tidsinställning. Det finns alltså inget sätt att vara säker på när jobbets run-metod kommer att köras i förhållande till den tråd som har skapat och schemalagt jobbet. Utdata blir antingen:
About to schedule a job This is a job Finished scheduling a jobeller:
About to schedule a job Finished scheduling a job This is a job
Om du vill vara säker på att ett jobb är slutfört innan du fortsätter kan du använda metoden join(). Med den metoden kommer anroparen att spärras tills jobbet är slutfört eller tills den anropande tråden avbryts. Vi provar att skriva om kodstycket ovan på ett mer deterministiskt sätt:
TrivialJob job = new TrivialJob(); System.out.println("About to schedule a job"); job.schedule(); job.join(); if (job.getResult().isOk()) System.out.println("Job completed with success"); else System.out.println("Job did not complete successfully");Förutsatt att anropet join() inte avbryts kommer den här metoden garanterat att returnera följande resultat:
About to schedule a job This is a job Job completed with success
Naturligtvis är det i allmänhet ingen mening att koppla ett jobb direkt efter det att det schemaläggs eftersom du inte får någon samtidighet på det sättet. I det här fallet kan du lika gärna göra det från jobbets run-metod direkt i anropstråden. Lite senare kommer vi att studera några exempel på när det är mer praktiskt att använda kopplingar.
Det senaste kodstycket utnyttjar också resultatet av jobbet. Resultatet är objektet IStatus som returneras från jobbets run()-metod. Du kan använda det här resultatet till att skicka tillbaka alla nödvändiga objekt från jobbets run-metod. Resultatet kan också användas till att ange misslyckande (genom att returnera IStatus med allvarlighetsgraden IStatus.ERROR), eller till att avbryta jobbet (IStatus.CANCEL).
Vi har sett hur vi schemalägger ett jobb och väntar på att det slutförs, men det finns mycket annat vi kan göra med jobb. Om du schemalägger ett jobb och sedan bestämmer att det inte längre behövs kan du stoppa jobbet med metoden cancel(). Om jobbkörningen inte har startat ännu när du avbryter det ignoreras jobbet direkt och kommer inte att köras. Om det däremot har börjat köras beror det på jobbet om det svarar på anropet om att avbrytas. När du försöker avbryta ett jobb är det praktiskt att använda metoden join() medan du väntar på det. Här är ett uttryck som du kan använda till att avbryta ett jobb och vänta tills det är klart innan du fortsätter:
if (!job.cancel()) job.join();
Om det inte avbryts omedelbart kommer cancel() att returnera false och anroparen använder join() till att vänta på att jobbet avbryts.
Ett mindre drastiskt sätt att avbryta är metoden sleep(). Även med den här metoden gäller att jobbet avbryts om det inte har börjat köras ännu. Jobbet finns kvar i minnet på plattformen, och anropet wakeUp() kommer att göra att jobbet läggs till i väntekön där det slutligen kommer att köras.
Ett jobb passerar olika lägen under sin livstid. Det kan ändras genom API till exempel med cancel() och sleep(), men jobbets läge ändras också när plattformen körs och slutför jobbet. Jobb kan förflytta sig genom följande lägen:
Ett jobb kan endast sättas i läget sleep om det för tillfället är i läget WAITING. När det tas ur viloläget sätts det tillbaka i läget WAITING. När ett jobb avbryts återgår det till läget NONE.
Om insticksprogrammet kräver information om läget för ett visst jobb kan det registrera en lyssningsfunktion för jobbändring som aviseras när jobbet förflyttar sig genom lägena. Det är ett praktiskt sätt att visa förloppet eller på annat sätt rapportera om ett jobb.
Jobb-metoden addJobChangeListener kan användas till att registrera en lyssningsfunktion för ett viss jobb. IJobChangeListener definierar protokoll som reagerar på lägesändringar för ett jobb:
I alla de här fallen tillhandahålls lyssningsfunktionen med en IJobChangeEvent där det jobb som går igenom lägesändring anges liksom status för slutförandet (om det är slutfört).
Obs! Jobben definierar också metoden getState() för att hämta det aktuella läget för ett jobb. Det här resultatet är dock inte alltid tillförlitligt eftersom jobben körs i en annan tråd och kan ändra läge igen innan anropet returneras. Lyssningsfunktioner för jobbändringar är den mekanism som rekommenderas för information om lägesändringar för ett jobb.
IJobManager definierar protokoll för arbete med alla jobben i systemet. Insticksprogram som visar förloppet eller på annat sätt arbetar med jobbinfrastrukturen kan använda IJobManager till att utföra uppgifter som att avbryta alla jobb i systemet, ta reda på vilka jobb som körs eller ta emot feedback om förlopp för ett visst jobb. Plattformens jobbhanterare kan hämtas med hjälp av plattforms-API:
IJobManager jobMan = Platform.getJobManager();
När insticksprogrammen behöver information om läget för alla jobb i systemet är det praktiskt att registrera en lyssningsfunktion för jobbändringar i jobbhanteraren i stället för att registrera lyssningsfunktioner för många enskilda jobb.
Ibland är det enklare för ett insticksprogram att arbeta med en grupp relaterade jobb som en enda enhet. Det går att göra med hjälp av jobbfamiljer. Ett jobb deklarerar att det hör till en viss familj genom att åsidosätta metoden belongsTo:
public static final String MY_FAMILY = "myJobFamily"; ... class FamilyJob extends Job { ... public boolean belongsTo(Object family) { return family == MY_FAMILY; } }Protokollet IJobManager kan användas till att avbryta, koppla ihop, och hitta alla jobb i en familj eller att sätta dem i viloläge:
IJobManager jobMan = Platform.getJobManager(); jobMan.cancel(MY_FAMILY); jobMan.join(MY_FAMILY, null);
Eftersom jobbfamiljer representeras med hjälp av slumpmässiga objekt kan du lagra ett läge i själva jobbfamiljen, och jobben kan bilda familjeobjekt dynamiskt efter behov. Det är viktigt att använda familjeobjekt som är tillräckligt unika, så att de inte råkar i konflikt med familjer som har skapats av andra insticksprogram.
Familjer är också ett praktiskt sätt att hitta grupper av jobb. Metoden IJobManager.find(Object family) kan användas till att söka efter förekomster av alla jobb som körs, väntar eller vilar när som helst.