1/25 Paměťový model Javy GUI v Javě Vlákna a JNI Vláknové programování část V Lukáš Hejmánek, Petr Holub {xhejtman,hopet}@ics.muni.cz Laboratoř pokročilých síťových technologií PV192 2014–03–25 2/25 Paměťový model Javy GUI v Javě Vlákna a JNI Přehled přednášky Paměťový model Javy GUI v Javě Vlákna a JNI 3/25 Paměťový model Javy GUI v Javě Vlákna a JNI long promenna = 10000000L; 4/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy happens-before ◾ částečné uspořádání Tabulka převzata z JCiP, Goetz 5/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy Tabulka převzata z JCiP, Goetz 6/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy Piggybacking ◾ spojení happens-before pravidla s jiným pravidlem, obvykle monitorem nebo volatile ◾ raději nepoužívat ◾ příklad: http://kickjava.com/src/java/util/ concurrent/FutureTask.java.htm postaveno na tryReleaseShared happens-before tryAcquireShared kombinace volatilní proměnné runner, do které tryReleaseShared zapisuje s program order 7/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy void innerSet(V v) { 2 for (;;) { int s = getState(); 4 if (ranOrCancelled(s)) return; 6 if (compareAndSetState(s, RAN)) break; 8 } result = v; 10 releaseShared(0); done(); 12 } V innerGet() throws InterruptedException JavaDoc, ExecutionException Jav 2 acquireSharedInterruptibly(0); if (getState() == CANCELLED) 4 throw new CancellationException JavaDoc(); if (exception != null) 6 throw new ExecutionException JavaDoc(exception); return result; 8 } 8/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy 1 protected int tryAcquireShared(int ignore) { return innerIsDone()? 1 : -1; 3 } 5 /** * Implements AQS base release to always signal after setting 7 * final done status by nulling runner thread. */ 9 protected boolean tryReleaseShared(int ignore) { runner = null; 11 return true; } 13 boolean innerIsCancelled() { 15 return getState() == CANCELLED; } 17 boolean innerIsDone() { 19 return ranOrCancelled(getState()) && runner == null; } 9/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy líná inicializace public class UnsafeLazyInitialization { 2 private static Resource resource; 4 public static Resource getInstance() { if (resource == null) 6 resource = new Resource(); // unsafe publication return resource; 8 } 10 static class Resource { } 12 } 10/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy @ThreadSafe 2 public class SafeLazyInitialization { private static Resource resource; 4 public synchronized static Resource getInstance() { 6 if (resource == null) resource = new Resource(); 8 return resource; } 10 static class Resource { 12 } } líná inicializace thread-safe 11/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy @ThreadSafe 2 public class EagerInitialization { private static Resource resource = new Resource(); 4 public static Resource getResource() { 6 return resource; } 8 static class Resource { 10 } } „dychtivá“ inicializace využívá skutečnosti, že statické inicializátory jsou vždy dokončeny před použitím třídy 12/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy @ThreadSafe 2 public class ResourceFactory { private static class ResourceHolder { 4 public static Resource resource = new Resource(); } 6 public static Resource getResource() { 8 return ResourceFactory.ResourceHolder.resource; } 10 static class Resource { 12 } } idiom líné inicializace s použitím holder class využívá líné inicializace tříd 13/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy public class DoubleCheckedLocking { 2 private static Resource resource; 4 public static Resource getInstance() { if (resource == null) { 6 synchronized (DoubleCheckedLocking.class) { if (resource == null) 8 resource = new Resource(); } 10 } return resource; 12 } 14/25 Paměťový model Javy GUI v Javě Vlákna a JNI Paměťový model Javy public class DoubleCheckedLocking { 2 private static Resource resource; 4 public static Resource getInstance() { if (resource == null) { 6 synchronized (DoubleCheckedLocking.class) { if (resource == null) 8 resource = new Resource(); } 10 } return resource; 12 } Double Checked Locking anti-pattern pomíjí možnost, že resource je v nedefinovaném stavu od Java 5.0 možno spravit použitím volatile nepoužívat ◾ ani v C/C++! 15/25 Paměťový model Javy GUI v Javě Vlákna a JNI Java SWING Multiplatformní GUI v Javě SWING single-thread rule ◾ všechny prvky mohou být vytvářeny, měněny a dotazovány pouze z vlákna obsluhujícího události ◾ SwingUtilities.isEventDispatchThread – kontrola, zda jsme ve vlákně obsluhující události ◾ SwingUtilities.invokeLater – předávání Runnable do vlákna obsluhujícího události ◾ SwingUtilities.invokeAndWait – předávání Runnable do vlákna obsluhujícího události a zablokuje se do dokončení akce ◾ callbacky se řeší pomocí akcí action listener z vlákna obsluhujícího události ◾ dlouho běžící callbacky je možno odštípnout do nového vlákna (přímo nebo přes Executory) 16/25 Paměťový model Javy GUI v Javě Vlákna a JNI Java SWING Inicializace public class JavaGUI { 2 public static void main(String[] args) { 4 CounterDialog dialog = new CounterDialog(); dialog.setSize(400,300); 6 dialog.setVisible(true); } 8 } 17/25 Paměťový model Javy GUI v Javě Vlákna a JNI Java SWING Předávání vláknu událostí SWINGu @Override 2 public void run() { for (int i = 0; i <= 1000; i++) { 4 if (shouldShutdown.get()) { break; 6 } final String labelText = String.valueOf(i); 8 javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { 10 counterLabel.setText(labelText); } 12 }); try { 14 Thread.sleep(1000); } catch (InterruptedException ignored) { 16 } } 18 runButton.setText("Run"); runButton.setEnabled(true); 20 } 18/25 Paměťový model Javy GUI v Javě Vlákna a JNI Java SWING Callbacky 1 buttonRun.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { 3 onRun(); } 5 }); private void onRun() { 2 if (!isRunning.get()) { buttonRun.setText("Stop"); 4 isRunning.set(true); if (counterThread != null) { 6 try { counterThread.join(); 8 } catch (InterruptedException ignored) { } 10 } counterThread = new CounterThread(counterLabel, buttonRun); 12 counterThread.start(); } else { 14 buttonRun.setEnabled(false); counterThread.requestShutdown(); 16 counterThread.interrupt(); isRunning.set(false); 18 } } 19/25 Paměťový model Javy GUI v Javě Vlákna a JNI Java Native Interfaces Volání nativních metod z Javy Struktura JNI volání /* C */ 2 JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj, jstring javaString) 4 { //ziskani nativniho retezce z javaString 6 const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0); ... 8 //nezapomenout uvolnit! (*env)->ReleaseStringUTFChars(env, javaString, nativeString); 10 } 12 // C++ JNIEXPORT void JNICALL Java_ClassName_MethodName 14 (JNIEnv *env, jobject obj, jstring javaString) { 16 //ziskani nativniho retezce z javaString const char *nativeString = env->GetStringUTFChars(javaString, 0); 18 ... //nezapomenout uvolnit! 20 env->ReleaseStringUTFChars(javaString, nativeString); } 20/25 Paměťový model Javy GUI v Javě Vlákna a JNI Vlákna a JNI Ukazatel na JNIEnv je platný pouze z vlákna, jemuž je přiřazen ◾ nelze předávat mezi vlákny ◾ stejný při opakovaných voláních v témže vlákně Lokální reference nesmí opustit vlákno ◾ lokální reference jsou platné pouze v rámci daného volání nelze se je uschovávat ve static proměnných ◾ převést na globální, pokud je třeba (NewGlobalRef) ◾ globální reference vylučují objekt z garbage collection ◾ existují slabé lokální reference (NewWeakGlobalRef) umožňují garbage collection odkazovaného objektu potřeba kvůli class unloading musí se kontrolovat, zda odkazovaný objekt existuje (IsSameObject s NULL parametrem) 21/25 Paměťový model Javy GUI v Javě Vlákna a JNI Vlákna a JNI static jclass stringClass = NULL; 2 ... if (stringClass == NULL) { 4 jclass localRefCls = (*env)->FindClass(env, "java/lang/String"); 6 if (localRefCls == NULL) { return NULL; /* exception thrown */ 8 } /* Create a global reference */ 10 stringClass = (*env)->NewGlobalRef(env, localRefCls); 12 /* The local reference is no longer useful */ (*env)->DeleteLocalRef(env, localRefCls); 14 /* Is the global reference created successfully? */ 16 if (stringClass == NULL) { return NULL; /* out of memory exception thrown */ 18 } } 20 ... // potreba explicitne mazat 22 if (terminate) { (*env)->DeleteGlobalRef(env, stringClass); 24 } Zdroj: JNI Book 22/25 Paměťový model Javy GUI v Javě Vlákna a JNI Vlákna a JNI Použití monitorů ◾ vždy monitor uvolnit synchronized (obj) { 2 ... // synchronized block } 1 if ((*env)->MonitorEnter(env, obj) != JNI_OK) ...; ... 3 if ((*env)->ExceptionOccurred(env)) { ... /* exception handling */ 5 /* remember to call MonitorExit here */ if ((*env)->MonitorExit(env, obj) != JNI_OK) ...; 7 } ... /* Normal execution path. */ 9 if ((*env)->MonitorExit(env, obj) != JNI_OK) ...; Zdroj: JNI Book 23/25 Paměťový model Javy GUI v Javě Vlákna a JNI Vlákna a JNI Wait/Notify ◾ generické volání metod (GetMethodID, CallVoidMethod) ◾ potřeba držet monitor 1 /* precomputed method IDs */ static jmethodID MID_Object_wait; 3 static jmethodID MID_Object_notify; static jmethodID MID_Object_notifyAll; 5 void 7 JNU_MonitorWait(JNIEnv *env, jobject object, jlong timeout) { (*env)->CallVoidMethod(env, object, MID_Object_wait, timeout); 9 } 11 void JNU_MonitorNotify(JNIEnv *env, jobject object) { 13 (*env)->CallVoidMethod(env, object, MID_Object_notify); } 15 void 17 JNU_MonitorNotifyAll(JNIEnv *env, jobject object) { (*env)->CallVoidMethod(env, object, MID_Object_notifyAll); 19 } Zdroj: JNI Book 24/25 Paměťový model Javy GUI v Javě Vlákna a JNI Vlákna a JNI Explicitní získání JNIenv pro stávající vlákno ◾ např. callback volaný OS ◾ odkaz na JavaVM lze předávat mezi voláními a vlákny ◾ získání odkazu např. JNI_GetCreatedJavaVMs nebo GetJavaVM 1 JavaVM *jvm; /* already set */ 3 f() { JNIEnv *env; 5 (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL); ... /* use env */ 7 } Zdroj: JNI Book 25/25 Paměťový model Javy GUI v Javě Vlákna a JNI Vlákna a JNI Mapování vláknového modelu OS a JVM ◾ záleží, zda pro danou platformu JVM podporuje nativní vlákna ◾ závisí od dané platformy i od daného JVM Můžeme implementovat v Javě afinitu k CPU ◾ závisí od dané platformy i od daného JVM ◾ sched_setaffinity(gettid(), ...) ◾ v praxi může pro danou platformu dobře fungovat