协调两个线程时的并发问题
Concurrency issue when coordinating two threads
我试图将一个循环的工作分成两个线程。
我正在使用 ExecutorService 创建第二个线程,同时将主线程用作另一个线程。
我正在使用计数器在达到某个值时停止循环,但我无法在这两个线程之间同步计数器并且循环是 运行 一个额外的时间。
此外,我在主线程中使用 while 循环来了解所需计数何时达到打印结束时间,这也不起作用。
ExecutorService executorService = Executors.newFixedThreadPool(1);
Clock clock = new Clock();
int count = 5;
AtomicInteger c = new AtomicInteger(1);
clock.start();
executorService.execute(()->{
while (c.get() <= count) {
System.out.println("LOOP ONE" + "----PRINT_ME");
c.incrementAndGet();
}
});
while (c.get() <= count) {
System.out.println("LOOP TWO" + "----PRINT_ME");
c.incrementAndGet();
}
while(true) {
if(c.get() == count) {
System.out.println("STOPPED");
clock.stop();
break;
}
}
输出-
Clock started at -- 2020-07-25T12:32:59.267
LOOP TWO----PRINT_ME
LOOP TWO----PRINT_ME
LOOP TWO----PRINT_ME
LOOP TWO----PRINT_ME
LOOP TWO----PRINT_ME
LOOP ONE----PRINT_ME
在上面的代码中,PRINT_ME 应该只打印 5 次(int count = 5;)但是却打印了 6 次(额外一次)。
我不确定这里发生了什么以及我们如何在两个线程之间共享计数。
PSI:如果我添加 Thread.sleep(2000);在两个循环中
executorService.execute(()->{
while (c.get() <= count) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("LOOP ONE" + "----PRINT_ME");
c.incrementAndGet();
}
});
while (c.get() <= count) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("LOOP TWO" + "----PRINT_ME");
c.incrementAndGet();
}
输出是:
Clock started at -- 2020-07-25T12:54:15.596
LOOP TWO----PRINT_ME
LOOP ONE----PRINT_ME
LOOP TWO----PRINT_ME
LOOP ONE----PRINT_ME
LOOP TWO----PRINT_ME
LOOP ONE----PRINT_ME
问题是您先轮询计数器,执行操作,然后递增计数器,表明作业已完成。
这会创建一个 window,其中两个线程可以同时执行工作并在完成后执行递增,但为时已晚。
为了更容易形象化,让我们假设所需的计数是 1,并且有两个线程在争抢要执行的任务。在您的实现中,两个线程都将从检查 c.get() <= count
开始,这对两者都是 true,然后执行导致您描述的问题的任务。
为了防止这种情况,线程需要先申请一个“空闲槽”,然后才执行任务。这可以通过在作业开始之前递增计数器来执行,然后在开始每个作业之前验证最大 count 条件:
int count = 5;
AtomicInteger c = new AtomicInteger(0);
executorService.execute(()->{
while (c.incrementAndGet() <= count) {
// ...
});
while (c.incrementAndGet() <= count) {
// ..
}
我试图将一个循环的工作分成两个线程。
我正在使用 ExecutorService 创建第二个线程,同时将主线程用作另一个线程。
我正在使用计数器在达到某个值时停止循环,但我无法在这两个线程之间同步计数器并且循环是 运行 一个额外的时间。
此外,我在主线程中使用 while 循环来了解所需计数何时达到打印结束时间,这也不起作用。
ExecutorService executorService = Executors.newFixedThreadPool(1);
Clock clock = new Clock();
int count = 5;
AtomicInteger c = new AtomicInteger(1);
clock.start();
executorService.execute(()->{
while (c.get() <= count) {
System.out.println("LOOP ONE" + "----PRINT_ME");
c.incrementAndGet();
}
});
while (c.get() <= count) {
System.out.println("LOOP TWO" + "----PRINT_ME");
c.incrementAndGet();
}
while(true) {
if(c.get() == count) {
System.out.println("STOPPED");
clock.stop();
break;
}
}
输出-
Clock started at -- 2020-07-25T12:32:59.267
LOOP TWO----PRINT_ME
LOOP TWO----PRINT_ME
LOOP TWO----PRINT_ME
LOOP TWO----PRINT_ME
LOOP TWO----PRINT_ME
LOOP ONE----PRINT_ME
在上面的代码中,PRINT_ME 应该只打印 5 次(int count = 5;)但是却打印了 6 次(额外一次)。
我不确定这里发生了什么以及我们如何在两个线程之间共享计数。
PSI:如果我添加 Thread.sleep(2000);在两个循环中
executorService.execute(()->{
while (c.get() <= count) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("LOOP ONE" + "----PRINT_ME");
c.incrementAndGet();
}
});
while (c.get() <= count) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("LOOP TWO" + "----PRINT_ME");
c.incrementAndGet();
}
输出是:
Clock started at -- 2020-07-25T12:54:15.596
LOOP TWO----PRINT_ME
LOOP ONE----PRINT_ME
LOOP TWO----PRINT_ME
LOOP ONE----PRINT_ME
LOOP TWO----PRINT_ME
LOOP ONE----PRINT_ME
问题是您先轮询计数器,执行操作,然后递增计数器,表明作业已完成。
这会创建一个 window,其中两个线程可以同时执行工作并在完成后执行递增,但为时已晚。
为了更容易形象化,让我们假设所需的计数是 1,并且有两个线程在争抢要执行的任务。在您的实现中,两个线程都将从检查 c.get() <= count
开始,这对两者都是 true,然后执行导致您描述的问题的任务。
为了防止这种情况,线程需要先申请一个“空闲槽”,然后才执行任务。这可以通过在作业开始之前递增计数器来执行,然后在开始每个作业之前验证最大 count 条件:
int count = 5;
AtomicInteger c = new AtomicInteger(0);
executorService.execute(()->{
while (c.incrementAndGet() <= count) {
// ...
});
while (c.incrementAndGet() <= count) {
// ..
}