我現在正在學習併發性,並且我試圖編寫一個應用程序,它應該演示使用併發收集時發生以前發生的關係。 如java.concurrent包指出:併發收集發生之前的關係
的java.util.concurrent中和所有類的方法及其子包 這些保證擴展到更高級別的同步。 特別是:在將對象放置到任何 併發收集之前的線程中的動作發生在訪問 之後的動作之前或從另一個線程中的集合中移除該元素。
我寫的下一類:
import java.util.Deque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;
public class HappensBefore {
static int checks = 0;
static int shouldNotHappen = 0;
static Deque<Integer> syncList = new LinkedBlockingDeque<>();
public static boolean varToHappenBefore = false; //this var must be false when new element added to collection
static AtomicBoolean flag = new AtomicBoolean();
static class SyncTask implements Runnable {
int localTemp = -1;
private final Thread t = new Thread(new Counter());
@Override
public void run() {
t.start();
while (syncList.isEmpty()) { //just skip
}
while (true) {
if (!Thread.interrupted()) {
if (flag.get()) {
int r = syncList.peekLast();
if (r != localTemp) {
if (varToHappenBefore) {
shouldNotHappen++;
}
varToHappenBefore = true;
localTemp = r;
checks++;
}
flag.set(false);
}
} else {
t.interrupt();
break;
}
}
}
}
static class Counter implements Runnable {
int ctr = 0;
@Override
public void run() {
while (!Thread.interrupted()) {
if (!flag.get()) {
flag.set(true);
varToHappenBefore = false;
syncList.add(ctr++);
}
}
}
}
public static void main(String[] args) throws InterruptedException {
SyncTask st = new SyncTask();
Thread s = new Thread(st);
s.start();
Thread.sleep(10000);//runtime ms
s.interrupt();
// s1.interrupt();
System.out.println("Elems times added: " + checks);
System.out
.println("Happens-before violated times: " + shouldNotHappen);
}
}
我做什麼推出線程1,這lauches線程2。 線程1檢查公共布爾值varToHappenBefore最初設置爲false。當thread1從集合中保存新的elem時,它將此布爾值設置爲true。在下一個新元素。如果此布爾值仍然爲真,則會發生 - 違規發生之前,shouldNotHappen將遞增。
線程1檢查併發收集是否有新元素,如果是,則將其保存在臨時變量var中,然後遞增總計數器檢查。 然後它切換爲讓thread2添加新元素的原子布爾值。在添加新元素之前,varToHappenBefore設置爲false。由於原子布爾標誌,在thread1執行之前,thread2不運行代碼。但是,在添加元素並檢查varToHappenBefore之前切換thread2中的標誌,因爲這兩個操作(elem add和boolean toggle)是通過原子布爾值同步的,否則。我確保在thread1運行後thread2只運行一次。如果varToHappenBefore發生在添加elem之前。然後在thread1中讀取(thread1僅在從集合中讀取新elem時檢查varToHappenBefore),然後在程序結束後,varToHappenBefore必須保持爲0。 但我得到下一個結果:
elems的時候還說:〜10 000 000
之前發生違反時間:0-10
也許我做了錯事,多線程是微妙而複雜。希望你的幫助。
編輯: 我要逃跑的情況時,線程1 setFlag(false)
之後它是真實的,並得到ELEM之前。從收集,因爲thread2然後可以從線程1中獲取元素和設置varToHappenBefore = true;
之間工作。如果我使AtomicBoolean.compareAndSet()
檢查塊,然後我得到80k失敗每8mil。它是可預測和清晰的。但是當我沒有爲thread2添加額外的元素來添加額外的元素來讀取集合和設置布爾值之間的時候,當布爾值爲true並且出現新元素時,我仍然會得到幾個順序讀取。
對於我來說,真的很難跟隨代碼所要做的事情。我注意到的一件事是你沒有使用['AtomicBoolean.compareAndSet()'](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicBoolean.html #compareAndSet%28boolean,%20boolean%29)用於更新標誌。相反,你使用單獨的'get'和'set'調用,這首先破壞了使用原子布爾值的目的,因爲沒有阻止值在調用之間改變。 –
另外,你有全局的'static'變量(例如'syncList'和'flag'),它們的狀態應該是''synchronized''。僅僅因爲你使用原子布爾值來存儲一個狀態,並不意味着你不需要同步訪問所有相互依賴的變量。 –
好吧,你可能是正確的AtomicBoolean.compareAndSet()。我做了修改,結果是一樣的。 – Natal