快看,i++真的不安全
阿新 • • 發佈:2019-08-16
上期文章講到“i++;”本身是一個執行緒不安全的操作,原因是操作不是原子性的,存在取值和賦值的兩個過程,但是究竟怎麼會不安全呢?本期藉助一個“vmlens”的專案來演示為何會發生執行緒不安全的情況。文末是vmlens簡介。
測試程式碼:
public class TestCounter { private volatile int i = 0; @Interleave public void increment() { i++; } @Test public void testUpdate() throws InterruptedException { Thread first = new Thread( () -> {increment();} ) ; Thread second = new Thread( () -> {increment();} ) ; first.start(); second.start(); first.join(); second.join(); } @After public void checkResult() { assertEquals( 2 , i ); } }
重要的是pom.xml檔案配置:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.vmlens</groupId> <artifactId>examples</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>examples</name> <url>http://maven.apache.org</url> <pluginRepositories> <pluginRepository> <id>vmlens</id> <url>http://vmlens.com/download</url> </pluginRepository> </pluginRepositories> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.vmlens</groupId> <artifactId>annotation</artifactId> <version>1.0.2</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.2</version> <scope>test</scope> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M3</version> <configuration> <includes> <include>none</include> </includes> </configuration> </plugin> <plugin> <groupId>com.vmlens</groupId> <artifactId>interleave</artifactId> <version>1.0.4</version> <!-- start regression test --> <configuration> <trimStackTrace>false</trimStackTrace> <includes> <include>com.vmlens.examples.doNotCombine.TestCounter</include> </includes> </configuration> </plugin> </plugins> </build> </project>
接下來是vmlens的報告:
從圖中我們可以看出在兩個執行緒同時執行“i++;”的時候,兩個執行緒都先後讀取到了i的值“0”,然後先後完成了計算“i+1”,最後又先後給i賦值“1”,導致測試用例執行失敗。
介紹一下這個外掛: “vmlens”是一款測試java多執行緒的工具。
- 需要測試多個執行緒訪問相同記憶體位置或監視器的應用程式的所有部分。vmlens顯示多個執行緒訪問相同記憶體位置或監視器的所有位置。
- vmlens插入等待,在測試期間通知指令並重新執行測試,直到測試所有執行緒交錯。這與資料競爭和死鎖的自動檢測一起導致系統和可重複的測試。
- 通過檢視多個執行緒以何種方式訪問相同狀態,您可以減少共享狀態的數量。
- 較少共享狀態意味著需要較少的同步監視器。
下面是作者托馬斯原文:
Hello!
Do you love to write bug-free software? Me too!
It always bothers me when I can not test something. That's why I created vmlens, a tool to test multithreaded java.
Now 4 years and countless tests later vmlens enables you to test multi-threaded java systematic and reproducible. And like vmlens now let my completely test vmlens, vmlens let you test the multithreaded part of your application
Enjoy writing concurrent software secured by tests.
Cheers, Thomas
作者本人照片: