disruptor performance issues when using two layers of multiple handlers in a pool - disruptor-pattern

i'm trying to use disruptor to process messages. i need two phases of processing.
i.e. two groups of handlers working in a worker pool like this (i guess):
disruptor.
handleEventsWithWorkerPool(
firstPhaseHandlers)
.thenHandleEventsWithWorkerPool(
secondPhaseHandlers);
when using the code above, if i put more than one worker in each group, the performance deteriorates. meaning tons of CPU wasted for the exact same amount of work.
i tried to tweak with the ring buffer size (which i already saw has an impact on performance) but in this case it didn't help. so am i doing something wrong, or is this a real problem?
i'm attaching a full demo of the issue.
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventTranslatorOneArg;
import com.lmax.disruptor.WorkHandler;
import com.lmax.disruptor.dsl.Disruptor;
final class ValueEvent {
private long value;
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
public final static EventFactory<ValueEvent> EVENT_FACTORY = new EventFactory<ValueEvent>() {
public ValueEvent newInstance() {
return new ValueEvent();
}
};
}
class MyWorkHandler implements WorkHandler<ValueEvent> {
AtomicLong workDone;
public MyWorkHandler (AtomicLong wd)
{
this.workDone=wd;
}
public void onEvent(final ValueEvent event) throws Exception {
workDone.incrementAndGet();
}
}
class My2ndPahseWorkHandler implements WorkHandler<ValueEvent> {
AtomicLong workDone;
public My2ndPahseWorkHandler (AtomicLong wd)
{
this.workDone=wd;
}
public void onEvent(final ValueEvent event) throws Exception {
workDone.incrementAndGet();
}
}
class MyEventTranslator implements EventTranslatorOneArg<ValueEvent, Long> {
#Override
public void translateTo(ValueEvent event, long sequence, Long value) {
event.setValue(value);
}
}
public class TwoPhaseDisruptor {
static AtomicLong workDone=new AtomicLong(0);
#SuppressWarnings("unchecked")
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
int numOfHandlersInEachGroup=Integer.parseInt(args[0]);
long eventCount=Long.parseLong(args[1]);
int ringBufferSize=2 << (Integer.parseInt(args[2]));
Disruptor<ValueEvent> disruptor = new Disruptor<ValueEvent>(
ValueEvent.EVENT_FACTORY, ringBufferSize,
exec);
ArrayList<MyWorkHandler> handlers = new ArrayList<MyWorkHandler>();
for (int i = 0; i < numOfHandlersInEachGroup ; i++) {
handlers.add(new MyWorkHandler(workDone));
}
ArrayList<My2ndPahseWorkHandler > phase2_handlers = new ArrayList<My2ndPahseWorkHandler >();
for (int i = 0; i < numOfHandlersInEachGroup; i++) {
phase2_handlers.add(new My2ndPahseWorkHandler(workDone));
}
disruptor
.handleEventsWithWorkerPool(
handlers.toArray(new WorkHandler[handlers.size()]))
.thenHandleEventsWithWorkerPool(
phase2_handlers.toArray(new WorkHandler[phase2_handlers.size()]));
long s = (System.currentTimeMillis());
disruptor.start();
MyEventTranslator myEventTranslator = new MyEventTranslator();
for (long i = 0; i < eventCount; i++) {
disruptor.publishEvent(myEventTranslator, i);
}
disruptor.shutdown();
exec.shutdown();
System.out.println("time spent "+ (System.currentTimeMillis() - s) + " ms");
System.out.println("amount of work done "+ workDone.get());
}
}
try running the above example with 1 thread in each group
1 100000 7
on my computer it gave
time spent 371 ms
amount of work done 200000
Then try it with 4 threads in each group
4 100000 7
which on my computer gave
time spent 9853 ms
amount of work done 200000
during the run the CPU is at 100% utilization

You seem to be false sharing the AtomicLong between the threads/cores. I'll try it out when I have more time later with a demo, however - much better would be to have each WorkHandler with a private variable that each thread owns (either it's own AtomicLong or preferably a plain long).
Update:
If you change your Disruptor line to:
Disruptor<ValueEvent> disruptor = new Disruptor<ValueEvent>(
ValueEvent.EVENT_FACTORY, ringBufferSize,
exec,
com.lmax.disruptor.dsl.ProducerType.SINGLE,
new com.lmax.disruptor.BusySpinWaitStrategy());
You'll get much better results:
jason#debian01:~/code/stackoverflow$ java -cp disruptor-3.1.1.jar:. TwoPhaseDisruptor 4 100000 1024
time spent 2728 ms
amount of work done 200000
I reviewed the code and tried to fix false sharing, but found little improvement. That's when I noticed on my 8core that the CPUs were nowhere near 100% (even for the four-worker test). From this I determined, at least, that a yielding/spinning wait strategy will bring reduced latency if you have CPU to burn.
Just make sure you have at least 8 cores (you'll need 8 for processing, plus one for publishing the messages).

Related

How to create a multi-threaded custom move factory in OptaPlanner?

I am solving a tough problem in OptaPlanner. The best algorithm I found so far is to use a custom move factory, a computationally intensive one. After noticing that I was utilising a single CPU core, I discovered that OptaPlanner only spreads on multiple threads the score calculation, while it performs the move generation in a single thread.
To mitigate the problem, I implemented the multi-threading in my move factory via the following abstract class, which I then extend with the actual logic (I did this because I actually have three computationally expensive custom move factories):
package my.solver.move;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.heuristic.move.CompositeMove;
import org.optaplanner.core.impl.heuristic.move.Move;
import org.optaplanner.core.impl.heuristic.selector.move.factory.MoveIteratorFactory;
import org.optaplanner.core.impl.score.director.ScoreDirector;
import java.util.Iterator;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
public abstract class MultiThreadedMoveFactory<T> implements MoveIteratorFactory<T> {
private final ThreadPoolExecutor threadPoolExecutor;
public MultiThreadedMoveFactory(
#NonNull String threadPrefix
) {
int availableProcessorCount = Runtime.getRuntime().availableProcessors();
int resolvedThreadCount = Math.max(1, availableProcessorCount);
ThreadFactory threadFactory = new SolverThreadFactory(threadPrefix);
threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(resolvedThreadCount, threadFactory);
}
#AllArgsConstructor
public class MoveGeneratorData {
T solution;
SolutionDescriptor<T> solutionDescriptor;
Random random;
BlockingQueue<Move<T>> generatedMoves;
}
protected abstract int getNumMoves();
#Override
public long getSize(ScoreDirector<T> scoreDirector) {
return getNumMoves();
}
protected class MovesIterator implements Iterator<Move<T>> {
private final BlockingQueue<Move<T>> generatedMoves = new ArrayBlockingQueue<>(getNumMoves());
public MovesIterator(
#NonNull T solution,
#NonNull SolutionDescriptor<T> solutionDescriptor,
#NonNull Random random,
#NonNull Function<MoveGeneratorData, Runnable> moveGeneratorFactory
) {
MoveGeneratorData moveGeneratorData = new MoveGeneratorData(solution, solutionDescriptor, random, generatedMoves);
for (int i = 0; i < getNumMoves(); i++) {
threadPoolExecutor.submit(moveGeneratorFactory.apply(moveGeneratorData));
}
}
#Override
public boolean hasNext() {
if (!generatedMoves.isEmpty()) {
return true;
}
while (threadPoolExecutor.getActiveCount() > 0) {
try {
//noinspection BusyWait
Thread.sleep(50);
} catch (InterruptedException e) {
return false;
}
}
return !generatedMoves.isEmpty();
}
#Override
public Move<T> next() {
//noinspection unchecked
return Objects.requireNonNullElseGet(generatedMoves.poll(), CompositeMove::new);
}
}
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private static class SolverThreadFactory implements ThreadFactory {
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public SolverThreadFactory(String threadPrefix) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = "MyPool-" + poolNumber.getAndIncrement() + "-" + threadPrefix + "-";
}
#Override
public Thread newThread(#NonNull Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (t.isDaemon()) {
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
#Override
public Iterator<? extends Move<T>> createOriginalMoveIterator(ScoreDirector<T> scoreDirector) {
return createMoveIterator(scoreDirector, new Random());
}
#Override
public Iterator<? extends Move<T>> createRandomMoveIterator(ScoreDirector<T> scoreDirector, Random workingRandom) {
return createMoveIterator(scoreDirector, workingRandom);
}
public abstract Iterator<? extends Move<T>> createMoveIterator(ScoreDirector<T> scoreDirector, Random random);
}
However, the solver seems to hang after a while. The debugger tells me that it's waiting on an innerQueue.take() in OrderByMoveIndexBlockingQueue. This is caused by my move factory: if I revert the above and only use the previous implementation, which was single-threaded, the problem goes away.
I do not quite understand where the problem is, so the question is: how can I fix it?
No, no, no. This approach is doomed. I think. (Prove me wrong.)
JIT selection
First learn about Just In Time selection (see docs) of moves.
Instead of generating all moves (which can be billions) at the beginning of each step, only generate those that will actually be evaluated. Most LS algorithms will only evaluate a few moves per step.
Watch the TRACE log to see how many milliseconds it takes to start a step. Typically you want to do evaluate 10000 moves per second, so it should take 0 or 1 milliseconds to start a step (the log only shows in milliseconds).
Multithreaded solving
Then learn about moveThreadCount to enable multithreaded solving. See this blog post. Know that this still does the move selection on 1 thread, for reproducibility reasons. But the move evaluation is spread across threads.
Caching for move selection
But your custom moves are smart, so the move selection must be smart?
First determine what "solution state" query information you need to generate the moves - for example a Map<Employee, List<Shift>> - then cache that:
either calculate that map at the beginning of each step, if it doesn't take too long (but this won't scale because it doesn't do deltas)
or use a shadow variable (#InverseRelationShadowVariable works fine in this case), because these are updated through deltas. But it does do the delta's for every move and undo move too...
Or hack in an actual new MoveSelector, which can listen to stepEnded() events and actually apply the delta of the last step on that Map, without doing any of the deltas of every move and undo move. We should probably standardize this approach and make it part of our public API some day.
I was able to make the factory work by removing any trace of JIT-ing from hasNext: block the method until all moves have been generated, and only then return true, and keep returning true until all moves have been consumed.
#Override
public boolean hasNext() {
while (!generationComplete && generatedMoves.size() < getNumMoves()) {
try {
// We get a warning because the event we are waiting for could happen earlier than the end of sleep
// and that means we would be wasting time, but that is negligible so we silence it
//noinspection BusyWait
Thread.sleep(50);
} catch (InterruptedException e) {
return false;
}
}
generationComplete = true;
return !generatedMoves.isEmpty();
}
To the best of my understanding, the solution I am using not only works, but it is the best I found in a few months of iterations.

While modifying ArrayList with one thread and iterating it with another thread, it is throwing ConcurrentModificationException

I was trying below code.
public class IteratorFailFastTest {
private List<Integer> list = new ArrayList<>();
public IteratorFailFastTest() {
for (int i = 0; i < 10; i++) {
list.add(i);
}
}
public void runUpdateThread() {
Thread thread2 = new Thread(new Runnable() {
public void run() {
for (int i = 10; i < 20; i++) {
list.add(i);
}
}
});
thread2.start();
}
public void runIteratorThread() {
Thread thread1 = new Thread(new Runnable() {
public void run() {
ListIterator<Integer> iterator = list.listIterator();
while (iterator.hasNext()) {
Integer number = iterator.next();
System.out.println(number);
}
}
});
thread1.start();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
IteratorFailFastTest tester = new IteratorFailFastTest();
tester.runIteratorThread();
tester.runUpdateThread();
}
}
It is throwing ConcurrentModificationException sometimes and at times running successfully.
What I don't understand is, since there are 2 different methods each containing one thread. They will execute one by one. When one thread finishes modifying the list, Thread 2 will start iterating.
I also referred to this link(Why no ConcurrentModificationException when one thread iterating (using Iterator) and other thread modifying same copy of non-thread-safe ArrayList), but it is different scenario.
So, Why is it throwing this exception? Is it because of threads?
Can somebody explain?
You are starting two threads and then doing no further synchronization.
Sometimes, both threads will be running at the same time, and you will get the CME. Other times, the first thread will finish before the second thread actually starts executing. In that scenario won't get a CME.
The reason you get the variation could well be down to things like load on your system. Or it could simply be down to the fact that the thread scheduler is non-deterministic.
Your threads actually do a tiny amount of work, compared to the overheads of creating / starting a thread. So it is not surprising that one of them can return from its run() method very quickly.

GroovyShell in Java8 : memory leak / duplicated classes [src code + load test provided]

We have a memory leak caused by GroovyShell/ Groovy scripts (see GroovyEvaluator code at the end). Main problems are (copy-paste from MAT analyser):
The class "java.beans.ThreadGroupContext", loaded by "<system class
loader>", occupies 807,406,960 (33.38%) bytes.
and:
16 instances of
"org.codehaus.groovy.reflection.ClassInfo$ClassInfoSet$Segment",
loaded by "sun.misc.Launcher$AppClassLoader # 0x7004e9c80" occupy
1,510,256,544 (62.44%) bytes
We're using Groovy 2.3.11 and Java8 (1.8.0_25 to be exact).
Upgrading to Groovy 2.4.6 doesn't solve the problem. Just improves memory usage a little bit, esp. non-heap.
Java args we're using: -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC
BTW, I've read https://dzone.com/articles/groovyshell-and-memory-leaks. We do set GroovyShell shell to null when it's no longer needed. Using GroovyShell().parse() would probably help but it isn't really an option for us - we have >10 sets, each consisting of 20-100 scripts, and they can be changed at any time (on runtime).
Setting MaxMetaspaceSize should also help, but it doesn't really solve the root problem, doesn't remove the root cause. So I'm still trying to nail it down.
I created load test to recreate the problem (see the code at the end). When I run it:
heap size, metaspace size and number of classes keep increasing
heap dump taken after several minutes is bigger than 4GB
Performance charts for first 3 minutes:
As I've already mentioned I'm using MAT to analyse heap dumps. So let's check Dominator tree report:
Hashmap takes > 30% of the heap.
So let's analyse it further. Let's see what sits inside it. Let's check hash entries:
It reports 38 830 entiries. Including 38 780 entries with keys matching ".class Script."
Another thing, "duplicate classes" report:
We have 400 entries (because load tests defines 400 G.scripts), all for "ScriptN" classes.
All of them holding references to groovyclassloader$innerloader
I've found similar bug reported: https://issues.apache.org/jira/browse/GROOVY-7498 (see comments at the end and attached screenshot) - their problems were solved by upgrading Java to 1.8u51. It didn't do a trick for us though.
Our code:
public class GroovyEvaluator
{
private GroovyShell shell;
public GroovyEvaluator()
{
this(Collections.<String, Object>emptyMap());
}
public GroovyEvaluator(final Map<String, Object> contextVariables)
{
shell = new GroovyShell();
for (Map.Entry<String, Object> contextVariable : contextVariables.entrySet())
{
shell.setVariable(contextVariable.getKey(), contextVariable.getValue());
}
}
public void setVariables(final Map<String, Object> answers)
{
for (Map.Entry<String, Object> questionAndAnswer : answers.entrySet())
{
String questionId = questionAndAnswer.getKey();
Object answer = questionAndAnswer.getValue();
shell.setVariable(questionId, answer);
}
}
public Object evaluateExpression(String expression)
{
return shell.evaluate(expression);
}
public void setVariable(final String name, final Object value)
{
shell.setVariable(name, value);
}
public void close()
{
shell = null;
}
}
Load test:
/** Run using -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC */
public class GroovyEvaluatorLoadTest
{
private static int NUMBER_OF_QUESTIONS = 400;
private final Map<String, Object> contextVariables = Collections.emptyMap();
private List<Fact> factMappings = new ArrayList<>();
public GroovyEvaluatorLoadTest()
{
for (int i=0; i<NUMBER_OF_QUESTIONS; i++)
{
factMappings.add(new Fact("fact" + i, "question" + i));
}
}
private void callEvaluateExpression(int iter)
{
GroovyEvaluator groovyEvaluator = new GroovyEvaluator(contextVariables);
Map<String, Object> factValues = new HashMap<>();
Map<String, Object> answers = new HashMap<>();
for (int i=0; i<NUMBER_OF_QUESTIONS; i++)
{
factValues.put("fact" + i, iter + "-fact-value-" + i);
answers.put("question" + i, iter + "-answer-" + i);
}
groovyEvaluator.setVariables(answers);
groovyEvaluator.setVariable("answers", answers);
groovyEvaluator.setVariable("facts", factValues);
for (Fact fact : factMappings)
{
groovyEvaluator.evaluateExpression(fact.mapping);
}
groovyEvaluator.close();
}
public static void main(String [] args)
{
GroovyEvaluatorLoadTest test = new GroovyEvaluatorLoadTest();
for (int i=0; i<995000; i++)
{
test.callEvaluateExpression(i);
}
test.callEvaluateExpression(0);
}
}
public class Fact
{
public final String factId;
public final String mapping;
public Fact(final String factId, final String mapping)
{
this.factId = factId;
this.mapping = mapping;
}
}
Any thoughts?
Thx in advance
OK, this is my solution:
public class GroovyEvaluator
{
private static GroovyScriptCachingBuilder groovyScriptCachingBuilder = new GroovyScriptCachingBuilder();
private Map<String, Object> variables = new HashMap<>();
public GroovyEvaluator()
{
this(Collections.<String, Object>emptyMap());
}
public GroovyEvaluator(final Map<String, Object> contextVariables)
{
variables.putAll(contextVariables);
}
public void setVariables(final Map<String, Object> answers)
{
variables.putAll(answers);
}
public void setVariable(final String name, final Object value)
{
variables.put(name, value);
}
public Object evaluateExpression(String expression)
{
final Binding binding = new Binding();
for (Map.Entry<String, Object> varEntry : variables.entrySet())
{
binding.setProperty(varEntry.getKey(), varEntry.getValue());
}
Script script = groovyScriptCachingBuilder.getScript(expression);
synchronized (script)
{
script.setBinding(binding);
return script.run();
}
}
}
public class GroovyScriptCachingBuilder
{
private GroovyShell shell = new GroovyShell();
private Map<String, Script> scripts = new HashMap<>();
public Script getScript(final String expression)
{
Script script;
if (scripts.containsKey(expression))
{
script = scripts.get(expression);
}
else
{
script = shell.parse(expression);
scripts.put(expression, script);
}
return script;
}
}
New solution keeps number of loaded classes and Metadata size at a constant level. Non-heap allocated memory usage = ~70 MB.
Also: there is no need to use UseConcMarkSweepGC anymore. You can choose whichever GC you want or stick with a default one :)
Synchronising access to script objects might not the best option, but the only one I found that keeps Metaspace size within reasonable level. And even better - it keeps it constant. Still. It might not be the best solution for everyone but works great for us. We have big sets of tiny scripts which means this solution is (pretty much) scalable.
Let's see some STATS for GroovyEvaluatorLoadTest with GroovyEvaluator using:
old approach with shell.evaluate(expression):
0 iterations took 5.03 s
100 iterations took 285.185 s
200 iterations took 821.307 s
script.setBinding(binding):
0 iterations took 4.524 s
100 iterations took 19.291 s
200 iterations took 33.44 s
300 iterations took 47.791 s
400 iterations took 62.086 s
500 iterations took 77.329 s
So additional advantage is: it's lightning fast compared to previous, leaking solution ;)

JavaFX: How to bind two values?

I'm new guy here :)
I have a small problem which concerns binding in JavaFX. I have created Task which is working as a clock and returns value which has to be set in a special label (label_Time). This label presents how many seconds left for player's answer in quiz.
The problem is how to automatically change value in label using the timer task? I tried to link value from timer Task (seconds) to label_Time value in such a way...
label_Time.textProperty().bind(timer.getSeconds());
...but it doesn't work. Is it any way to do this thing?
Thanks in advance for your answer! :)
Initialize method in Controller class:
public void initialize(URL url, ResourceBundle rb) {
Timer2 timer = new Timer2();
label_Time.textProperty().bind(timer.getSeconds());
new Thread(timer).start();
}
Task class "Timer2":
public class Timer2 extends Task{
private static final int SLEEP_TIME = 1000;
private static int sec;
private StringProperty seconds;
public Timer2(){
Timer2.sec = 180;
this.seconds = new SimpleStringProperty("180");
}
#Override protected StringProperty call() throws Exception {
int iterations;
for (iterations = 0; iterations < 1000; iterations++) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
System.out.println("TIK! " + sec);
seconds.setValue(String.valueOf(sec));
System.out.println("TAK! " + seconds.getValue());
// From the counter we subtract one second
sec--;
//Block the thread for a short time, but be sure
//to check the InterruptedException for cancellation
try {
Thread.sleep(10);
} catch (InterruptedException interrupted) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
}
}
return seconds;
}
public StringProperty getSeconds(){
return this.seconds;
}
}
Why your app does not work
What is happening is that you run the task on it's own thread, set the seconds property in the task, then the binding triggers an immediate update of the label text while still on the task thread.
This violates a rule for JavaFX thread processing:
An application must attach nodes to a Scene, and modify nodes that are already attached to a Scene, on the JavaFX Application Thread.
This is the reason that your originally posted program does not work.
How to fix it
To modify your original program so that it will work, wrap the modification of the property in the task inside a Platform.runLater construct:
Platform.runLater(new Runnable() {
#Override public void run() {
System.out.println("TIK! " + sec);
seconds.setValue(String.valueOf(sec));
System.out.println("TAK! " + seconds.getValue());
}
});
This ensures that when you write out to the property, you are already on the JavaFX application thread, so that when the subsequent change fires for the bound label text, that change will also occur on the JavaFX application thread.
On Property Naming Conventions
It is true that the program does not correspond to JavaFX bean conventions as Matthew points out. Conforming to those conventions is both useful in making the program more readily understandable and also for making use of things like the PropertyValueFactory which reflect on property method names to allow table and list cells to automatically update their values as the underlying property is updated. However, for your example, not following JavaFX bean conventions does not explain why the program does not work.
Alternate Solution
Here is an alternate solution to your countdown binding problem which uses the JavaFX animation framework rather than the concurrency framework. I prefer this because it keeps everything on the JavaFX application thread and you don't need to worry about concurrency issues which are difficult to understand and debug.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.*;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CountdownTimer extends Application {
#Override public void start(final Stage stage) throws Exception {
final CountDown countdown = new CountDown(10);
final CountDownLabel countdownLabel = new CountDownLabel(countdown);
final Button countdownButton = new Button(" Start ");
countdownButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent t) {
countdownButton.setText("Restart");
countdown.start();
}
});
VBox layout = new VBox(10);
layout.getChildren().addAll(countdownLabel, countdownButton);
layout.setAlignment(Pos.BASELINE_RIGHT);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20; -fx-font-size: 20;");
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
class CountDownLabel extends Label {
public CountDownLabel(final CountDown countdown) {
textProperty().bind(Bindings.format("%3d", countdown.timeLeftProperty()));
}
}
class CountDown {
private final ReadOnlyIntegerWrapper timeLeft;
private final ReadOnlyDoubleWrapper timeLeftDouble;
private final Timeline timeline;
public ReadOnlyIntegerProperty timeLeftProperty() {
return timeLeft.getReadOnlyProperty();
}
public CountDown(final int time) {
timeLeft = new ReadOnlyIntegerWrapper(time);
timeLeftDouble = new ReadOnlyDoubleWrapper(time);
timeline = new Timeline(
new KeyFrame(
Duration.ZERO,
new KeyValue(timeLeftDouble, time)
),
new KeyFrame(
Duration.seconds(time),
new KeyValue(timeLeftDouble, 0)
)
);
timeLeftDouble.addListener(new InvalidationListener() {
#Override public void invalidated(Observable o) {
timeLeft.set((int) Math.ceil(timeLeftDouble.get()));
}
});
}
public void start() {
timeline.playFromStart();
}
}
Update for additional questions on Task execution strategy
Is it possible to run more than one Task which includes a Platform.runLater(new Runnable()) method ?
Yes, you can use multiple tasks. Each task can be of the same type or a different type.
You can create a single thread and run each task on the thread sequentially, or you can create multiple threads and run the tasks in parallel.
For managing multiple tasks, you can create an overseer Task. Sometimes it is appropriate to use a Service for managing the multiple tasks and the Executors framework for managing multiple threads.
There is an example of a Task, Service, Executors co-ordination approach: Creating multiple parallel tasks by a single service In each task.
In each task you can place no runlater call, a single runlater call or multiple runlater calls.
So there is a great deal of flexibility available.
Or maybe I should create one general task which will be only take data from other Tasks and updating a UI?
Yes you can use a co-ordinating task approach like this if complexity warrants it. There is an example of such an approach in in Render 300 charts off screen and save them to files.
Your "Timer2" class doesn't conform to the JavaFX bean conventions:
public String getSeconds();
public void setSeconds(String seconds);
public StringProperty secondsProperty();

Java threading question

The following code SHOULD NOT print out the right balance (100), but it is printing out 100 every time for me. Why is that? The following code does not seem to be thread safe.
public class ThreadObject implements Runnable{
private int balance;
public ThreadObject() {
super();
}
public void add() {
int i = balance;
balance = i + 1;
}
public void run() {
for(int i=0;i<50;i++) {
add();
System.out.println("balance is " + balance);
}
}
}
public class ThreadMain {
public static void main(String[] args) {
ThreadObject to1 = new ThreadObject();
Thread t1 = new Thread(to1);
Thread t2 = new Thread(to1);
t1.start();
t2.start();
}
}
If the following code is indeed thread safe, could you explain how?
Because it looks like the code in add() is not thread safe at all. One thread could be be setting i to the current balance, but then becomes inactive while the second thread takes over and updates the balance. Then thread one wakes up which is setting balance to an obsolete i plus 1.
The println is probably thousands of times slower than the code that updates the balance. Each thread spends almost all of its time printing, so the likelihood of them simultaneously updating the balance is very small.
Add a small sleep between reading i and writing i + 1.
Here's a dastardly question: What is the smallest possible value of i after running the above code?
Move your println a little upper to see that this is not thread-safe. If you still can't see any change make 50 bigger (like 5000 or more).
public void add() {
int i = balance;
System.out.println("balance is " + balance);
balance = i + 1;
}
public void run() {
for(int i=0;i<50;i++) {
add();
}
}

Resources