Java 当我注释掉System.out.println时,为什么这个阻塞队列代码不起作用?
下面的代码从未打印过Java 当我注释掉System.out.println时,为什么这个阻塞队列代码不起作用?,java,Java,下面的代码从未打印过Done。但是,如果我取消注释System.out.println(list.size())结束。为什么会发生这种情况 import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.LinkedBlock
Done
。但是,如果我取消注释System.out.println(list.size())代码>结束。为什么会发生这种情况
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
public class Test3 {
static BlockingQueue<String> blockingQueue;
static List<String> list = new ArrayList<>();
static void run() {
while (true) {
try {
String s = blockingQueue.take();
Thread.sleep(1000);
list.add(s);
}catch (InterruptedException e){}
}
}
public static void main(String[] args) {
blockingQueue = new LinkedBlockingQueue<>();
blockingQueue.add("test1");
blockingQueue.add("test2");
blockingQueue.add("test3");
CompletableFuture.runAsync(() -> run());
while (list.size() < 3) {
// uncomment the line below to make it finish
//System.out.println(list.size());
}
System.out.println("Done");
}
}
import java.util.ArrayList;
导入java.util.List;
导入java.util.concurrent.BlockingQueue;
导入java.util.concurrent.CompletableFuture;
导入java.util.concurrent.LinkedBlockingQueue;
公共类Test3{
静态阻塞队列阻塞队列;
静态列表=新的ArrayList();
静态无效运行(){
while(true){
试一试{
字符串s=blockingQueue.take();
睡眠(1000);
列表。添加(s);
}捕获(中断异常e){}
}
}
公共静态void main(字符串[]args){
blockingQueue=新建LinkedBlockingQueue();
blockingQueue.add(“test1”);
blockingQueue.add(“test2”);
blockingQueue.add(“test3”);
CompletableFuture.runAsync(()->run());
while(list.size()<3){
//取消注释下面的行,使其结束
//System.out.println(list.size());
}
系统输出打印项次(“完成”);
}
}
我似乎没有任何想法。根据:ArrayList不是线程安全的,引用一下:
请注意,此实现是不同步的。如果有多个线程
并发访问ArrayList实例,并至少访问
线程从结构上修改列表,它必须同步
外部。(A)结构修改是指添加或
删除一个或多个元素,或显式调整支持数组的大小;
仅仅设置一个元素的值并不是一个结构性的问题
这通常是通过在某些服务器上进行同步来实现的
对象,该对象自然地封装了列表。如果不存在这样的物体,
应使用Collections.synchronizedList“包装”列表
方法
要修复使用,请执行以下操作:
static List<String> list = java.util.Collections.synchronizedList(new ArrayList());
static List=java.util.Collections.synchronizedList(新的ArrayList());
编译器可以自由优化
while (list.size() < 3) {
// uncomment the line below to make it finish
//System.out.println(list.size());
}
while(list.size()<3){
//取消注释下面的行,使其结束
//System.out.println(list.size());
}
到
if(list.size()<3){
而(1),;
}
为什么??因为这看起来就像CPU正在从缓存中读取列表的大小,CPU可以拥有缓存并从中读取
请注意,并不是CPU实际上有一个缓存,或者任何东西实际上都是从缓存中读取的。只是CPU被允许有缓存并从中读取,而您的代码必须考虑到这一点,但您没有做到这一点
Java提供了任意数量的同步原语,可用于解决此问题
对于一种语言来说,这似乎是一个奇怪的决定。但是跨线程同步只在有限的情况下需要,并且假设不需要跨线程同步所允许的优化数量是巨大而重要的。因此,将责任放在需要同步来请求它的代码上
当您听到Java的内存语义描述为将缓存刷新到主内存时,请理解这是一种抽象。系统的行为就像它有需要刷新的缓存一样。同步功能的行为就像它们将数据刷新到主内存或从主内存读取数据一样。但是,在大多数系统上实际发生的情况是完全不同的,因为CPU缓存的一致性在硬件上是有保证的,在您可能运行Java代码的最现实的多核CPU上
这有点有趣,但这种神秘的缓存和刷新使代码更加高效,即使它实际上并不存在于现实的现代硬件中!事实上,程序员需要像存在这样的缓存一样进行操作,并且需要这样的刷新,这就允许在代码生成中进行优化,从而显著提高性能。谢谢!将其更改为CopyOnWriteArrayList有效。执行此操作将有效//Thread.sleep(1000)代码>@SvetlinZarev好的,谢谢。从编译器的角度来看,“list.size()”可能是一个函数调用(这里也有一些有用的答案)。只有在函数调用不包含线程同步代码的情况下,编译器才能以上面描述的方式优化函数调用,这在您输入的代码段中并不明显。。。因此,我建议将“free to optimize”调整为“free to optimize,如果函数不包含线程同步”@vladmihaisima,我不想这样说。编译器可以自由地对它们进行优化,即使它们确实包含线程同步,它只需以不破坏同步的方式对它们进行优化。事实上,我正在讨论添加另一段话,这段话与你的建议几乎相反。好了,这种优化实际上并不是这样应用的。“你有证据支持你的说法吗?”Markrotterveel仔细阅读了我的回答。我并没有说它实际上是这样应用的,我说编译器是“自由优化”的,而不是说它实际上是这样做的。这一差异是至关重要的。“编译器可以自由优化…”意味着它可以,否则您的回答的其余部分对我来说没有多大意义:如果编译器(或jit或JVM)不是这样工作的,那么缓存行为很可能是一个原因(但您说CPU缓存不是这样工作的)。
if (list.size() < 3) {
while(1);
}