• Tidak ada hasil yang ditemukan

Application-level concurrency: High-level concurrency with SwingWorker

N/A
N/A
Protected

Academic year: 2023

Membagikan "Application-level concurrency: High-level concurrency with SwingWorker"

Copied!
8
0
0

Teks penuh

(1)

Application-level concurrency: High-level concurrency with SwingWorker

A quick recap of Friday’s lecture We learned:

• Whatcritical sectionsandmutual exclusionare.

• The tradeoff made when making parts of the program mutually exclusive.

• Three ways of ensuring mutual exclusion.

– synchronized methods (least flexible) – synchronized statements

– Lockobjects (most flexible, require responsible use)

Further reading to understand memory semantics ofsynchronized: Double- checked locking: Clever, but broken http://www.javaworld.com/article/2074979/java- concurrency/double-checked-locking–clever–but-broken.html

Next step, high level concurrency in GUIs withSwingWorker!

“Single-threaded” GUI applications

While GUI applications certainly involve more than one thread, there are important restrictions regarding access to GUI component objects.

• For example: most Swing component methods must be invokedonlyfrom the Event Dispatch Thread (EDT).

• There are a few exceptions, mentioned in the API as “thread safe”.

• Programs that ignore these rules may appear to function correctly most of the time, but are subject to unpredictable errors!

• This is true not only for Swing, but for many modern GUI toolkits (Qt, NextStep, MacOS Cocoa, X Windows, . . . )

Multi-threaded GUI toolkits

• Graham Hamilton, Multithreaded toolkits: A failed dream?

https://community.oracle.com/blogs/kgh/2004/10/19/multithreaded-toolkits-failed-dream

“There are certain ideas in Computer Science that I think of as the ‘Failed Dreams’ [. . . ]. The Failed Dreams seem like obvious good ideas. So they get periodically reinvented, and people put a lot of time and thought into them. They typically work well on a research scale and they have the intriguing attribute of almost working on a

(2)

production scale. Except you can never quite get all the kinks ironed out...

For me, multithreaded GUI toolkits seem to be one of the Failed Dreams.”

Swing-specific threads

Initial thread : the thread that invokes main().

Event-dispatch thread : (EDT, aka the GUI thread) executes drawing and event handling code.Should not be used to run time-consuming tasks!

Worker (background) threads : for time-consuming tasks.Must not in- teract directly with the GUI.

Two high-level solutions exist for proper interaction between background threads and the EDT:

1. SwingUtilities 2. SwingWorker

SwingUtilities.invokeLater(Runnable r)

• Schedules a given task to be asynchronously executed by the Event Dispatch Thread and returns immediately (without waiting for the actual execution).

SwingUtilities.invokeLater(new Runnable() { public void run() {

// Code to be executed by the EDT }

});

• Typically invoked from a worker thread.

• Good practice to have the initial thread use this to create and start the GUI. (Again: only the EDT should interact directly with the GUI.) SwingUtilities.invokeAndWait(Runnable r)

• Just like SwingUtilities.invokeLater(), but evaluates the task syn- chronously. =⇒ calling thread waits until the EDT completes the task.

• Wrapping a statement in a runnable and callinginvokeAndWait()is sim- ilar to having the statement directly in the code - but only the former is safeif the statement updates the GUI.

(3)

• However,invokeAndWait()isinterruptable, just likeThread.sleep().

In this case, the task will still be completed by the EDT, but asyn- chronously.

• Also: cannot be called from the EDT. (Why not?) SwingWorker

SwingWorkeris an abstract class which encapsulates:

• One abstract method, doInBackground(), which defines the task that needs to run by an automatically-assigned background thread (no need to create this.)

• Two methods, process()and done(), which will be automatically exe- cutedby the EDT(at proper times).

SwingWorker

• ThedoInBackground()method will return afinal resultand, optionally, publishesa sequence ofinterim results.

• the EDT done()andprocess()methods are provided so that the EDT can display the final and interim results ofdoInBackground().

To use SwingWorker, you need to subclass it, and override some or all of the above methods.

SwingWorker

In addition,SwingWorkerprovides the following concrete methods:

• publish(), which transfers interim objects from the worker thread in doInBackground()to the EDT inprocess().

• get(), typically run in done(), which picks up the final result object created indoInBackground().

These two methods are final, i.e. not overridable.

(4)

SwingWorker

SwingWorker is an abstract generic classSwingWorker<T,V>, where

• Tis the type of thefinalresult of (and return type of)doInBackground(), which can be accessed usingget().

• V is the type of the interim objects published by doInBackground(), which are further transferred toprocess().

Note

If your task doesn’t directly return results, use type Void for the final and interim type variables.

SwingWorker - bird’s eye view

public ConcreteSwingWorkerextends SwingWorker<T,V> {

@Override

protected TdoInBackground() { // Run by background thread

// Optionally calls publish(V ... chunk) repeatedly // Finally, returns a T

}

@Override

protected void process(List<V>chunks) { // Run by EDT

// Gets V’s published by doInBackground };

@Override

protected void done() {

// Typically, calls get() to retrieve // final T returned by doInBackground() };

}

SwingWorker example

Consider classMySwingWorker<Double,Integer>where

• doInBackground()generates a sequence of Integers, for instance 0,1,2, . . . ,20.

• doInBackground()publishes these integers as interim values for the EDT toprocess().

• Finally,doInBackground()returns the average as aDouble(in this case 10.0).

• The EDT displays all interim and final values.

• The GUI must remain responsive at all times, even if the background process takes a long time to complete.

(5)

SwingWorker example - the GUI

Controls: checkButton, workerButton,noWorkerButton SwingWorker example - the GUI

• checkButtonsimply prints “GUI responsive” to the text area.

• workerButton starts the background execution of a MySwingWorker in- stance, disables the button and returns immediately.

• noWorkerButtonperforms the same task as MySwingWorker, butin the EDT.

MySwingWorker code: doInBackground()

(6)

public MySwingWorker extendsSwingWorker<Double,Integer> { // ...

protected Double doInBackground() { intk=0;

doublesum = 0.0;

for(int i=0;i<=20;i++) { publish(i);

k+= 1;

sum+= i;

try{

if (i%3 == 0)

Thread.sleep(1000);

}catch(InterruptedException ex) { } }

returnsum/k;

} // ...

}

MySwingWorker.java

MySwingWorker code: process()

public MySwingWorker extends SwingWorker<Double,Integer> { // ...

@Override

protected void process(List<Integer> nums) { // EDT for (int i : nums)

textArea.append(i + " ");

textArea.append("\n");

}

// ...

}

MySwingWorker.java

MySwingWorker code: done()

public MySwingWorker extends SwingWorker<Double,Integer> { // ...

@Override

protected void done() { // EDT double avg = 0.0;

try {

avg = get();

} catch (InterruptedException ex) { } catch (ExecutionException ex) { }

textArea.append("... done: average="

+ avg + "\n");

workerButton.setEnabled(true);

} }

MySwingWorker.java

(7)

SwingWorker Example: workerButton handler

A click on workerButton starts the background execution of a MySwing- Worker, disables the button and returns immediately (to keep the GUI respon- sive):

public void actionPerformed(ActionEvent e) { workerButton.setEnabled(false);

MySwingWorker worker =newMySwingWorker(textArea,workerButton);

worker.execute();

}

SwingWorkerExample.java

Attention

• Don’t calldoInBackground()directly; instead callexecute()which causes a background thread to calldoInBackground().

• Don’t try toexecute()a SwingWorker instance more than once. Create a new one each time.

Inversion of Control

SwingWorkeruses a familiar (by now) design pattern:

• Youdefine the methodsdoInBackground(), process()anddone().

• You don’t invoke them yourself (not directly anyway).

• The framework will schedule their execution, in the EDT thread (as appropriate).

This is a nice example of IoC!

Executor and ExecutorService Potential problems

• Threads are expensive to create.

• Too many threads running concurrently can make the system run slowly.

To address these problems, Java uses theExecutorinterface to abstract the process of thread management away from that of task execution:

public interfaceExecutor { void execute(Runnable r);

}

Exactly what happens to theRunnableis implementation detail!

TheExecutorServiceinterface extendsExecutorand is similar, but allows submission of tasks (Callables) that return a value.

(8)

Fixed-size thread pools

• A usefulExecutorServiceis a fixed-size thread pool, which can be cre- ated like this:

ExecutorService threadPool =Executors.newFixedThreadPool(5);

• Any implementation of theCallableinterface can be submitted viathreadPool.submit().

• If a thread is available, the task will be executed immediately. If all are occupied, the task will be added to a queue until one becomes available.

• Guess what? SwingWorkerimplementsCallable! The following therefore works:

threadPool.submit(mySwingWorker);

• CallingmySwingWorker.execute()actually submits the worker task to a default/private thread pool (usually has a maximum size of 10). If you need more control, it’s better to use anExecutorService.

Summary

Today, we have learned about:

• Swing-specific threads - initial thread, EDT and worker threads,

• The dangers of modifying the GUI from threads other than the EDT,

• Use of SwingUtilities.invokeLater() to schedule tasks for execution by the EDT,

• Use ofSwingWorkers to schedule tasks for executionoutsideof the EDT, and

• Finer-grained control over howSwingWorkerscheduling occurs usingExecutorServices.

Referensi

Dokumen terkait