import com.sun.javafx.image.ByteToBytePixelConverter; import com.sun.javafx.image.PixelGetter; import com.sun.javafx.image.impl.ByteBgra; import com.sun.javafx.image.impl.ByteBgraPre; import javafx.application.Application; import javafx.application.Platform; import javafx.stage.Stage; public class ImageRace extends Application { volatile boolean ready = false; volatile boolean t1Running = false; volatile boolean t2Running = false; ByteToBytePixelConverter conv; PixelGetter getter; void initByteBgra() { conv = ByteBgra.ToByteBgraPreConverter; } void initByteBgraPre() { getter = ByteBgraPre.getter; } @Override public void start(Stage stage) { Thread t1 = new Thread(() -> { System.err.println("thread 1 started"); t1Running = true; while (!ready) {} initByteBgra(); System.err.println("thread 1 done"); }); Thread t2 = new Thread(() -> { System.err.println("thread 2 started"); t2Running = true; while (!ready) {} initByteBgraPre(); System.err.println("thread 2 done"); }); // If we initialize either of the ByteBgra* classes from the main thread // then there is no deadlock. Similarly, if we run thread 1 and thread 2 // serially there is no deadlock. // This will workaround the issue // try { // Class.forName("com.sun.javafx.image.impl.ByteBgra"); // } catch (ClassNotFoundException ignore) { // } t1.start(); t2.start(); while (!t1Running) {} while (!t2Running) {} System.err.println("[main] signal the threads to proceed"); try { Thread.sleep(100); } catch (InterruptedException ex) {} ready = true; try { Thread.sleep(100); t1.join(); t2.join(); } catch (InterruptedException ex) {} Platform.exit(); } public static void main(String[] args) { Application.launch(args); } }