infer 是facebook 出品的程式碼靜態檢查工具,這個工具中的racerd是可以用來檢查java 執行緒中的data race問題。
執行方式檢查單個的java檔案
infer --racerd-only -- javac File.java
檢查android的專案
./gradlew clean #清除編譯結果,方便檢查所有的結果
infer --racerd-only -- ./gradlew build
檢查出來的結果比如下面的程式碼
@ThreadSafe
class Publisher implements Runnable {
private ConnectionFactory factory = new ConnectionFactory();
private Channel channel;
private Queue<String> queue = new LinkedList<String>();
private boolean isInited;
public void init() {
if(isInited) {
return;
}
.........
}
@Override
public void run() {
init();
while(true) {
查下來log 如下
app/src/main/java/com/example/systeminfo/ui/notifications/NotificationsFragment.java:77: warning: THREAD_SAFETY_VIOLATION
Read/Write race. Non-private method `void NotificationsFragment$Publisher.init()` reads without synchronization from `this.isInited`. Potentially races with write in method `NotificationsFragment$Publisher.init()`.
Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself).
75.
76. public void init() {
77. > if(isInited) {
78. return;
79. }
app/src/main/java/com/example/systeminfo/ui/notifications/NotificationsFragment.java:84: warning: THREAD_SAFETY_VIOLATION
Unprotected write. Non-private method `void NotificationsFragment$Publisher.init()` writes to field `this.isInited` outside of synchronization.
Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself).
82. Connection conn = factory.newConnection();
83. channel = conn.createChannel();
84. > isInited = true;
85. } catch (URISyntaxException e) {
86. e.printStackTrace();
app/src/main/java/com/example/systeminfo/ui/notifications/NotificationsFragment.java:83: warning: THREAD_SAFETY_VIOLATION
Unprotected write. Non-private method `void NotificationsFragment$Publisher.init()` writes to field `this.channel` outside of synchronization.
Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself).
81. factory.setUri("amqp://user:[email protected]:5672/%2f");
82. Connection conn = factory.newConnection();
83. > channel = conn.createChannel();
84. isInited = true;
85. } catch (URISyntaxException e) {
發現init函式裡面有個isInited 可能不是執行緒安全的,實際上init函式並不會在run以外被呼叫,但是由於是介面是public的有可能被人誤用了,安全的做法就是把這個介面改成private的
infer 檢查如何起作用infer的RacerD 檢查並不是自然發生的,需要滿足兩個條件
(1)用@ThreadSafe顯式註解一個類/方法;
(2)通過synchronized關鍵字使用鎖。
在這兩種情況下,RacerD會在包含訊號及其所有依賴的程式碼中尋找併發問題。特別是,它將報告同一類的任何非私有方法之間的競合,這些方法可能出現訪問衝突。用@ThreadSafe註釋一個類/介面,也會觸發對該類/介面的所有子類/實現的檢查。
infer 還有其他的一些註解也會觸發檢查,如 GuardBy等