Bas*_*ian 15 java multithreading
We are using the ScheduledExecutorService.scheduleAtFixedRate to perform an I/O Task every 100ms. (The actual I/O Operation is performed by a third party library and we don't know exactly what is happening inside.)
Sometimes keeping up with the 100ms intervals has some issues and we fall back to 500ms. Since it is I/O, we weren't surprised, but we observed a strange behaviour:
If a specific Thread runs, we are matching the 100ms. If this Thread is not running, we are falling back to 500ms.
That specific Threads' run-method looks like this:
while(active){
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
//some more stuff
}
Run Code Online (Sandbox Code Playgroud)
Basically when we use a short delay like 5ms we get a better performance within the library. When we use longer delays like 1000 ms, performance is significally worse.
It also seems to be platform specific since we were not able to reproduce the problem. (Java 8, Windows 10)
All what we know is that it's definitely the short sleep()-call causing the improvement since we could fix the issue by running a dummy thread just sleeping in short intervals.
Any explanation would be helpful to understand what is happening :-)
--- edit
Even more interesting: if we are adding not one but two new Threads only for short sleep intervals, it adds a little bit of performance. Not as significant as the first thread, but still like 20%.
--- edit 2
The System where we can observe the behaviour: Intel Atom E3845 Windows 10
The System where we can't reproduce it: Intel i7-5820K Windows 10
We can't look up the source code but the library seems to run single threaded (no new threads created) and creates a Socket connection.
这可能是一个多线程可见性问题(在不知道所涉及的源/库的情况下很难说)。
该示例基于线程how-to-demonstrate-java-multithreading-visibility-problems中的代码。
执行以下代码将导致线程无限执行t。
public class Test extends Thread {
boolean keepRunning = true;
public static void main(String[] args) throws InterruptedException {
Test t = new Test();
t.start();
Thread.sleep(1000);
t.keepRunning = false;
System.out.println(System.currentTimeMillis() + ": keepRunning is false");
}
public void run() {
int i = 0;
while (keepRunning) {
i++;
}
System.out.println("iterations: " + i);
}
}
Run Code Online (Sandbox Code Playgroud)
原因是线程t保留其自己的本地状态keepRunning并且永远不会获取更新的状态。
如果将该run方法修改为
public void run() {
int i = 0;
while (keepRunning) {
i++;
System.out.println(i);
}
System.out.println("iterations: " + i);
}
Run Code Online (Sandbox Code Playgroud)
例如,它结束于
...
111955
111956
iterations: 111956
1582797899956: keepRunning is false
Run Code Online (Sandbox Code Playgroud)
问:这个附加语句如何println改变行为?A:System.out是 aPrintStream并且该println方法实现为
public void println(boolean x) {
synchronized (this) {
print(x);
newLine();
}
}
Run Code Online (Sandbox Code Playgroud)
该synchronized块导致线程本地状态和主内存之间的同步。
当您使用 aThread.sleep(delay)代替时,会发生类似的效果。
public void run() {
int i = 0;
while (keepRunning) {
i++;
try {
Thread.sleep(5);
} catch(InterruptedException e) {
System.out.println("interrupted");
Thread.currentThread().interrupt();
}
}
System.out.println("iterations: " + i);
}
Run Code Online (Sandbox Code Playgroud)
示例输出
1582798987660: keepRunning is false
iterations: 197
Run Code Online (Sandbox Code Playgroud)
Thread.sleep(delay)您可以尝试用语句替换 the ,System.out.println()看看是否会产生类似的效果。那么很可能与上面的例子有关。
编辑要强制线程t获取更新状态,可以将变量keepRunning声明为volatile.
公共类测试扩展线程{
volatile boolean keepRunning = true;
public static void main(String[] args) throws InterruptedException {
Test t = new Test();
t.start();
Thread.sleep(1000);
t.keepRunning = false;
System.out.println(System.currentTimeMillis() + ": keepRunning is false");
}
public void run() {
int i = 0;
while (keepRunning) {
i++;
}
System.out.println("iterations: " + i);
}
Run Code Online (Sandbox Code Playgroud)
}
示例输出
iterations: 1726941412
1582799612746: keepRunning is false
Run Code Online (Sandbox Code Playgroud)