{"id":19,"date":"2007-02-09T21:00:44","date_gmt":"2007-02-09T21:00:44","guid":{"rendered":"http:\/\/euve3303.vserver.de\/stefan\/blog\/?p=21"},"modified":"2007-02-09T21:00:44","modified_gmt":"2007-02-09T21:00:44","slug":"testing-concurrent-java-programs","status":"publish","type":"post","link":"https:\/\/cogito-ergo-blog.de\/blog\/2007\/02\/09\/testing-concurrent-java-programs\/","title":{"rendered":"Testing Concurrent Java Programs"},"content":{"rendered":"<p>Beim Testen von Java Programmen die Nebenl\u00e4ufigkeiten und Synchronisation enthalten, bekommt man schnell Probleme. Zum einen stellt man fest, dass es recht schwierig ist Testf\u00e4lle zu konstruieren, die alle relevanten Codeteile durchlaufen. Und selbst wenn das geschafft ist, kann man noch nicht sicher sein, dass das Programm sich unter realen Bedingung korrekt verh\u00e4lt.<\/p>\n<p>Die zeitlichen Anl\u00e4ufe, der nicht vorhersagbare Wettbewerb vieler Threads lassen sich schlecht oder gar nicht simulieren. Selbst wenn man Tests entwirft, die mit mehreren Threads arbeiten, stellt man schnell fest, das die Testabl\u00e4ufe fast vollkommen deterministisch sind und das zuf\u00e4llige Zeitverhalten einer echten Anwendung in keiner Weise erreicht wird. Deshalb findet man mit solchen &quot;fast&quot; deterministischen Tests auch potentielle Deadlocks oder Race-Conditions meistens nicht. Diese Probleme tauchen dann erst im Betrieb auf &#8230;<\/p>\n<h3>Zufall einf\u00fcgen<\/h3>\n<p>Was hilft, ist den Test weniger vorhersagbar zu machen, das Zeitverhalten zu variieren. Das kann man erreichen, in dem man etwas &quot;Zufall&quot; in den Code einf\u00fcgt.<\/p>\n<p><!--more--><\/p>\n<h3>Bytecode Instrumentation<\/h3>\n<p>Um nicht den Quellcode oder Testcode mit &quot;Zufalls&quot;-Anweisungen zu zusch\u00fctten, kann man auch den Weg der Bytecode Instrumentation w\u00e4hlen. Hier wird der fertig \u00fcbersetzte Code vorzugsweise beim Laden in die JavaVM mit Zufallsanweisungen angereichert.<\/p>\n<p>Von den verschiedenen Frameworks zur Bytecode Manipulation scheint mir ASM das flexibelste zu sein. Zwar lassen sich nicht alle Konstruktionen so elegant ausdr\u00fccken wie in anderen Frameworks, aber daf\u00fcr hat man kaum Einschr\u00e4nkungen.<\/p>\n<h3>Transforming ClassLoader<\/h3>\n<p>Mit Hilfe eines ClassLoaders, der die zu testenden Klassen l\u00e4dt und einer Basis-Klasse die TestCase erweitert und als Basis f\u00fcr Concurrent-Tests dient, ist eine einfache JUnit Integration m\u00f6glich.<\/p>\n<blockquote style=\"border: 1px solid gray; overflow: auto; font-size: 100%; height: 300px; background-color: rgb(238, 238, 238);\"><p>\n<tt class=\"java\"><span class=\"java4\">package <\/span><span class=\"java10\">rinke.solutions.tool.test;<\/p>\n<p><\/span><span class=\"java4\">import <\/span><span class=\"java10\">java.io.IOException;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">java.util.HashSet;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">java.util.Set;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">java.util.regex.Pattern;<\/p>\n<p><\/span><span class=\"java4\">import <\/span><span class=\"java10\">org.apache.commons.logging.Log;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">org.apache.commons.logging.LogFactory;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">org.objectweb.asm.ClassAdapter;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">org.objectweb.asm.ClassReader;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">org.objectweb.asm.ClassWriter;<\/p>\n<p><\/span><span class=\"java4\">public class <\/span><span class=\"java10\">TransformingClassLoader <\/span><span class=\"java4\">extends <\/span><span class=\"java10\">ClassLoader <\/span><span class=\"java8\">{<\/p>\n<p>&#xA0; <\/span><span class=\"java10\">Set&lt;String&gt; whiteList = <\/span><span class=\"java4\">new <\/span><span class=\"java10\">HashSet&lt;String&gt;<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0; Set&lt;String&gt; blackList = <\/span><span class=\"java4\">new <\/span><span class=\"java10\">HashSet&lt;String&gt;<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0; <\/span><span class=\"java4\">private static <\/span><span class=\"java10\">Log log = LogFactory.getLog<\/span><span class=\"java8\">(<\/span><span class=\"java10\">TransformingClassLoader.<\/span><span class=\"java4\">class<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0; <\/span><span class=\"java4\">public <\/span><span class=\"java10\">TransformingClassLoader<\/span><span class=\"java8\">() {<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; blackList.add<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;java\\\\..*&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; blackList.add<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;javax\\\\..*&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; blackList.add<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;sun\\\\..*&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0; <\/span><span class=\"java4\">protected <\/span><span class=\"java10\">Class&lt;?&gt; loadAndInstrumentClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">String name<\/span><span class=\"java8\">)<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">throws <\/span><span class=\"java10\">ClassNotFoundException <\/span><span class=\"java8\">{<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">log.debug<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;loading class &#34; <\/span><span class=\"java10\">+ name + <\/span><span class=\"java5\">&#34; ...&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">try <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">ClassReader cr = <\/span><span class=\"java4\">new <\/span><span class=\"java10\">ClassReader<\/span><span class=\"java8\">(<\/span><span class=\"java10\">name<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; ClassWriter cw = <\/span><span class=\"java4\">new <\/span><span class=\"java10\">ClassWriter<\/span><span class=\"java8\">(<\/span><span class=\"java4\">true<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; ClassAdapter ca = <\/span><span class=\"java4\">new <\/span><span class=\"java10\">ConcurrentClassAdapter<\/span><span class=\"java8\">(<\/span><span class=\"java10\">cw<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; cr.accept<\/span><span class=\"java8\">(<\/span><span class=\"java10\">ca, <\/span><span class=\"java4\">false<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java9\">byte<\/span><span class=\"java8\">[] <\/span><span class=\"java10\">bytecode = cw.toByteArray<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return <\/span><span class=\"java10\">defineClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">name, bytecode, <\/span><span class=\"java7\">0<\/span><span class=\"java10\">, bytecode.length<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">} <\/span><span class=\"java4\">catch <\/span><span class=\"java8\">(<\/span><span class=\"java10\">IOException e<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">throw new <\/span><span class=\"java10\">ClassNotFoundException<\/span><span class=\"java8\">(<\/span><span class=\"java10\">e.getMessage<\/span><span class=\"java8\">()<\/span><span class=\"java10\">, e<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0; }<\/p>\n<p>&#xA0; <\/span><span class=\"java16\">@Override<br \/>\n&#xA0; <\/span><span class=\"java4\">public <\/span><span class=\"java10\">Class&lt;?&gt; loadClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">String name<\/span><span class=\"java8\">) <\/span><span class=\"java4\">throws <\/span><span class=\"java10\">ClassNotFoundException <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if <\/span><span class=\"java8\">(<\/span><span class=\"java10\">inWhiteList<\/span><span class=\"java8\">(<\/span><span class=\"java10\">name<\/span><span class=\"java8\">) ) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return <\/span><span class=\"java10\">loadAndInstrumentClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">name<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">} <\/span><span class=\"java4\">else if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">inBlackList<\/span><span class=\"java8\">(<\/span><span class=\"java10\">name<\/span><span class=\"java8\">)) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return super<\/span><span class=\"java10\">.loadClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">name<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">} <\/span><span class=\"java4\">else <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">whiteList.size<\/span><span class=\"java8\">() <\/span><span class=\"java10\">&gt; <\/span><span class=\"java7\">0 <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return super<\/span><span class=\"java10\">.loadClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">name<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">} <\/span><span class=\"java4\">else <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return <\/span><span class=\"java10\">loadAndInstrumentClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">name<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0; }<br \/>\n&#xA0; }<\/p>\n<p>&#xA0; <\/span><span class=\"java4\">private <\/span><span class=\"java9\">boolean <\/span><span class=\"java10\">inWhiteList<\/span><span class=\"java8\">(<\/span><span class=\"java10\">String name<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">whiteList.size<\/span><span class=\"java8\">() <\/span><span class=\"java10\">&gt; <\/span><span class=\"java7\">0 <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">for <\/span><span class=\"java8\">(<\/span><span class=\"java10\">String pat : whiteList<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">Pattern.matches<\/span><span class=\"java8\">(<\/span><span class=\"java10\">pat, name<\/span><span class=\"java8\">)) <\/span><span class=\"java4\">return true<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0; }<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return false<\/span><span class=\"java10\">;<br \/>\n&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0; <\/span><span class=\"java4\">private <\/span><span class=\"java9\">boolean <\/span><span class=\"java10\">inBlackList<\/span><span class=\"java8\">(<\/span><span class=\"java10\">String name<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">for<\/span><span class=\"java8\">(<\/span><span class=\"java10\">String pat : blackList<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">Pattern.matches<\/span><span class=\"java8\">(<\/span><span class=\"java10\">pat, name<\/span><span class=\"java8\">)) <\/span><span class=\"java4\">return true<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return false<\/span><span class=\"java10\">;<br \/>\n&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>}<\/span><\/tt>\n<\/p><\/blockquote>\n<p>Wie man sieht kennt der ClassLoader eine BlackList und eine WhiteList mit der sich \u00fcber RegEx steuern l\u00e4\u00dft welche Klassen instrumentiert werden sollen und welche nicht. Ausserdem beachtet der ConcurrentClassAdapter eine Annotation, die auf Class-Level gesetzt, das instrumentieren einer Klasse verhindert.<\/p>\n<h3>ConcurrentClassAdapter<\/h3>\n<p>Der ConcurrentClassAdapter der in ASM typischer Weise in den Eventstrom zwischen Reader und Writer geschaltet wird, hat nun folgende Aufgaben:<\/p>\n<p>1. Wird f\u00fcr jede Methode ein spezieller CodeAdapter eingef\u00fcgt.<\/p>\n<p>2. Synchronized Methoden werden mit einem Wrapper versehen, der den Aufruf der synchronized Methode abf\u00e4ngt und vor und nach dem Aufruf der urspr\u00fcnglichen Methode &quot;Zufall&quot; einf\u00fcgt.<\/p>\n<p>3. Wird die Annotation &quot;DontInstrument&quot; auf Class-Level gefunden, werden alle weiteren Aktionen \u00fcbersprungen und die Klasse normal geladen.<\/p>\n<p>Code siehe Archiv-Download am Ende &#8230;<\/p>\n<h3>ConcurrentCodeAdapter<\/h3>\n<p>Der CodeAdapter wird f\u00fcr jede Methode gerufen und h\u00e4lt Ausschau nach Aufrufen von wait(), nach dem Betreten oder Verlassen von Synchronized-Bl\u00f6cken oder nach dem Aufruf von &quot;synchronized&quot;-Methoden. Immer dann wenn ein solche Punkte im Bytecode erreicht ist, der aus der Sicht der Concurrency interessant ist, wird ein Aufruf an der Concurrency-Runtime eingef\u00fcgt. Diese Runtime steuert den Zufall und protokolliert was das Programm (die Threads) gerade tun.<\/p>\n<p>Code-Abschnitt der das Betreten und Verlassen von Synchronized-Bl\u00f6cken instrumentiert:<\/p>\n<blockquote style=\"border: 1px solid gray; overflow: auto; font-size: 100%; height: 300px; background-color: rgb(238, 238, 238);\"><p>\n<tt class=\"java\"><span class=\"java0\">&#xA0; <\/span><span class=\"java16\">@Override<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">public <\/span><span class=\"java9\">void <\/span><span class=\"java10\">visitInsn<\/span><span class=\"java8\">(<\/span><span class=\"java9\">int <\/span><span class=\"java10\">opcode<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">opcode == MONITORENTER <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java10\">.visitLdcInsn<\/span><span class=\"java8\">(<\/span><span class=\"java10\">source + <\/span><span class=\"java5\">&#34;: &#34; <\/span><span class=\"java10\">+actLine<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java10\">.visitMethodInsn<\/span><span class=\"java8\">(<\/span><span class=\"java10\">INVOKESTATIC, runtimeClass, <\/span><span class=\"java5\">&#34;monitorEnter&#34;<\/span><span class=\"java10\">, <\/span><span class=\"java5\">&#34;(Ljava\/lang\/Object;Ljava\/lang\/String;)Ljava\/lang\/Object;&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ dup lock <br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java10\">.visitInsn<\/span><span class=\"java8\">(<\/span><span class=\"java10\">DUP<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java10\">.visitInsn<\/span><span class=\"java8\">(<\/span><span class=\"java10\">opcode<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java10\">.visitLdcInsn<\/span><span class=\"java8\">(<\/span><span class=\"java10\">source + <\/span><span class=\"java5\">&#34;: &#34; <\/span><span class=\"java10\">+actLine<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java10\">.visitMethodInsn<\/span><span class=\"java8\">(<\/span><span class=\"java10\">INVOKESTATIC, runtimeClass, <\/span><span class=\"java5\">&#34;monitorEntered&#34;<\/span><span class=\"java10\">, <\/span><span class=\"java5\">&#34;(Ljava\/lang\/Object;Ljava\/lang\/String;)V&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">} <\/span><span class=\"java4\">else if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">opcode == MONITOREXIT <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java10\">.visitLdcInsn<\/span><span class=\"java8\">(<\/span><span class=\"java10\">source + <\/span><span class=\"java5\">&#34;: &#34; <\/span><span class=\"java10\">+actLine<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java10\">.visitMethodInsn<\/span><span class=\"java8\">(<\/span><span class=\"java10\">INVOKESTATIC, runtimeClass, <\/span><span class=\"java5\">&#34;monitorExit&#34;<\/span><span class=\"java10\">, <\/span><span class=\"java5\">&#34;(Ljava\/lang\/Object;Ljava\/lang\/String;)Ljava\/lang\/Object;&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java10\">.visitInsn<\/span><span class=\"java8\">(<\/span><span class=\"java10\">opcode<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">} <\/span><span class=\"java4\">else <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java10\">.visitInsn<\/span><span class=\"java8\">(<\/span><span class=\"java10\">opcode<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0; }<br \/>\n<\/span><\/tt><\/p><\/blockquote>\n<h3>ConcurrentRuntime<\/h3>\n<p>Die Runtime Umgebung macht nicht viel mehr als bei allen relevanten Stellen Zufall in Form von zuf\u00e4lligen sleep(xxx) oder yield() einzuf\u00fcgen (einstellbar).<\/p>\n<p>Dar\u00fcber hinaus werden die Threads, die bestimmte Locks besitzen oder darauf warten protokolliert, so dass der Concurrency-Zustand immer bekannt ist. Weiterhin misst die Runtime sowas wie die Concurrency-Coverage, pr\u00fcft also ob Threads von Synchronized-Bl\u00f6cken blockiert wurden oder ob das Halten von Locks in einer synchronized Methode an anderer Stelle zum Warten gef\u00fchrt hat.<\/p>\n<h3>Download<\/h3>\n<p>Falls Interesse besteht kann ich die kompletten Klassen hier zum Download anbieten.<\/p>\n<p>Bislang hat sich einer gemeldet \ud83d\ude09 also <a href=\"http:\/\/www.stefan-rinke.de\/download\/RandomTest-1.0.zip\">hier<\/a> gibts das komplette Projekt. Dependencies sind <a href=\"http:\/\/asm.objectweb.org\/\">asm<\/a>-all und commons-logging.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Beim Testen von Java Programmen die Nebenl\u00e4ufigkeiten und Synchronisation enthalten, bekommt man schnell Probleme. Zum einen stellt man fest, dass es recht schwierig ist Testf\u00e4lle zu konstruieren, die alle relevanten Codeteile durchlaufen. Und selbst wenn das geschafft ist, kann man &hellip; <a href=\"https:\/\/cogito-ergo-blog.de\/blog\/2007\/02\/09\/testing-concurrent-java-programs\/\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"_links":{"self":[{"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/posts\/19"}],"collection":[{"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/comments?post=19"}],"version-history":[{"count":0,"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/posts\/19\/revisions"}],"wp:attachment":[{"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/media?parent=19"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/categories?post=19"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/tags?post=19"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}