Java 并发收集发生在关系之前
我现在正在学习并发性,我试图编写一个程序,演示在使用并发集合时发生在之前的关系。 如java.concurrent包中所述: java.util.concurrent及其子包中所有类的方法 将这些保证扩展到更高级别的同步。在里面 特殊:将对象放入任何线程之前线程中的操作 并发收集发生在访问之后的操作之前 或者从另一个线程的集合中删除该元素 我写了下一节课:Java 并发收集发生在关系之前,java,multithreading,concurrency,concurrent-collections,happens-before,Java,Multithreading,Concurrency,Concurrent Collections,Happens Before,我现在正在学习并发性,我试图编写一个程序,演示在使用并发集合时发生在之前的关系。 如java.concurrent包中所述: java.util.concurrent及其子包中所有类的方法 将这些保证扩展到更高级别的同步。在里面 特殊:将对象放入任何线程之前线程中的操作 并发收集发生在访问之后的操作之前 或者从另一个线程的集合中删除该元素 我写了下一节课: import java.util.Deque; import java.util.concurrent.LinkedBlockingDequ
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);
}
}
import java.util.Deque;
导入java.util.concurrent.LinkedBlockingDeque;
导入java.util.concurrent.AtomicBoolean;
公共课以前发生过{
静态整数检查=0;
静态int应不出现=0;
static Deque syncList=new LinkedBlockingDeque();
public static boolean varToHappenBefore=false;//将新元素添加到集合时,此变量必须为false
静态AtomicBoolean标志=新的AtomicBoolean();
静态类SyncTask实现可运行{
int localTemp=-1;
私有最终线程t=新线程(新计数器());
@凌驾
公开募捐{
t、 start();
而(syncList.isEmpty()){//只需跳过
}
while(true){
如果(!Thread.interrupted()){
if(flag.get()){
int r=syncList.peekLast();
如果(r!=localTemp){
如果(之前发生了变化){
不应该出现++;
}
varToHappenBefore=true;
localTemp=r;
检查++;
}
flag.set(false);
}
}否则{
t、 中断();
打破
}
}
}
}
静态类计数器实现可运行{
int ctr=0;
@凌驾
公开募捐{
而(!Thread.interrupted()){
如果(!flag.get()){
flag.set(true);
varToHappenBefore=false;
syncList.add(ctr++);
}
}
}
}
公共静态void main(字符串[]args)引发InterruptedException{
SyncTask st=新的SyncTask();
螺纹s=新螺纹(st);
s、 start();
Thread.sleep(10000);//运行时ms
s、 中断();
//s1.中断();
System.out.println(“添加元素次数:“+检查”);
系统输出
.println(“发生在违反时间之前:+不应发生”);
}
}
我做的是启动thread1,它启动thread2。
Thread1在之前检查公共布尔值vartohappen>,该值最初设置为false。当thread1从集合中保存新元素时,它会将此布尔值设置为true。在下一个新元素上。receiving如果此布尔值仍然为true,则在发生冲突之前发生,shouldNotHappen递增
Thread1检查并发集合是否有新元素,如果有,则将其保存在临时变量中,并增加总体计数器检查次数。
然后切换原子布尔值,让thread2添加新元素。添加新元素之前,varToHappenBefore设置为false。由于原子布尔标志,thread2不会在thread1之前运行代码。但是,在thread2中切换标志是在添加元素并在之前检查vartohappenbour之前完成的,因为这两个操作(elem add和boolean toggle)是通过原子布尔同步的。我确保thread1运行后thread2只运行一次。如果在添加元素之前发生了变量。到线程2中的集合,然后在线程1中读取该集合(只有在从集合中读取新元素时,线程1才会检查vartohappenbfore),则vartohappenbfore在程序结束后必须保持为0。
但我得到了下一个结果:
埃姆斯时报补充说:~10000
发生在违反时间之前:0-10
可能我做错了什么,多线程是微妙而复杂的。希望你的帮助
编辑:
我需要在thread1
setFlag(false)
为真之后和获取元素之前,避免这种情况。因为thread2可以在从集合获取元素和设置varToHappenBefore=true之间工作代码>在线程1中。如果我使用AtomicBoolean.compareAndSet()
检查块,那么每8mils就有80k次失败。而且它是可预测的和明确的。但当我没有为thread2创建这样一个窗口来在从集合读取和设置布尔值之间添加额外的元素时,当布尔值为true并且出现新元素时,我仍然会得到很少的顺序读取。您的SyncTask
清除标志,即使它发现列表尚未更改。因此,您可能会错过列表添加事件。这使得您的假设不正确:
缺少事件后的下一轮,SyncTask
发现列表已更改,它假定计数器
已完成其任务,检查(成功)并在
之前重新启动vartohappen。但是Counter
实际上会在之后将元素添加到列表中
下一轮SyncTask
founds列表再次更改,它假设Counter
已完成其任务,在之前检查vartohappen并发现它未被清除。这只是因为计数器
尚未清除它
只需移动flag.set(false)如果(r!=localTemp)
块,则所有操作都将工作。您的SyncTask
清除标志
,即使它发现列表尚未更改。因此,您可能会错过列表添加事件。这使得您的假设不正确:
缺少事件后的下一轮,SyncTask
发现列表已更改,它假定计数器
已完成其任务,检查(成功)并在
之前重新启动vartohappen。日分
public class HappensBeforeTest {
private static boolean producerStopped;
private static final Queue<String> queue = new LinkedBlockingQueue<>();
//private static final Queue<String> queue = new LinkedList<>();
public static void main(String[] args) {
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
private static class Producer implements Runnable {
public void run() {
int count = 0;
while (count++ < 10) {
producerStopped = (count == 10);
queue.add(String.valueOf(count));
}
System.out.println("Producer finished");
}
}
private static class Consumer implements Runnable {
public void run() {
while (!producerStopped) {
queue.poll();
}
System.out.println("Consumer finished");
}
}