维基百科上列出了Java中的Singleton实现之一:
public class SingletonDemo {
private static volatile SingletonDemo instance = null;
private SingletonDemo() {
}
public static SingletonDemo getInstance() {
if (instance == null) {
synchronized (SingletonDemo.class) {
if (instance == null) {
instance = new SingletonDemo();
}
}
}
return instance;
}
}
Run Code Online (Sandbox Code Playgroud)
有Java语言规范17条第5款指出,
当构造函数完成时,对象被认为是完全初始化的.在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的最终字段的正确初始化值.
好吧,想象一下我们的SingletonDemo类有非final字段.那么,并发线程将能够读取默认值而不是构造函数中指定的正确值吗?
据我所知,对于旧的JMM,实现懒单线的DCL(双重检查锁定)技巧被打破了,但我认为它是用新的JMM和易变场固定的......
然而,在这篇很好的文章中,显然是新的,需要在DCL中引用新旧JMM和volatile字段,它仍然被打破......
在这里和那里我读到它是固定的然后我发现这个...有人可以说最后它是否破碎?
我的理解是,挥发性保证关系之前的hapens和有效发布membar解决问题,DCL现在有效...尽管我同意静态懒惰初始化更好,更容易理解...
While studying Java Memory Model, I was confused by the definition of synchronization order (SO). It is said that SO is a total order over all of the synchronization actions (SA) of an execution. But what is the point of talking about all SA of an execution? I can't figure out how this might be useful to me. How can I think about all SA? I understand the meaning of the following statement:
For each thread t, the synchronization …
假设我有以下代码:
final Catalog catalog = createCatalog();
for (int i = 0; i< 100; i++{
new Thread(new CatalogWorker(catalog)).start();
}
Run Code Online (Sandbox Code Playgroud)
"Catalog"是一个对象结构,而createCatalog()和"Catalog"对象结构的方法并没有考虑并发性.产品目录中有几个非最终的非易失性引用,甚至可能存在可变状态(但是必须要处理)
我理解内存模型的方式,这段代码不是线程安全的.有没有简单的方法让它安全?(这个问题的通用版本实际上是关于在线程爆炸之前创建的共享结构的单线程构造)
以下是ArrayList在多线程环境中同步工作的代码片段吗?
class MyList {
private final ArrayList<String> internalList = new ArrayList<String>();
void add(String newValue) {
synchronized (internalList) {
internalList.add(newValue);
}
}
boolean find(String match) {
synchronized (internalList) {
for (String value : internalList) {
if (value.equals(match)) {
return true;
}
}
}
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
我担心一个线程无法通过另一个线程看到更改.
是否可以在同步块内重新排序语句?例如
synchronized(lock) {
statement1;
statement2;
}
Run Code Online (Sandbox Code Playgroud)
其中,statement1和statement2不相互依赖.处理器或编译器可以重新排序这些语句吗?
谢谢.
你能用简单的词语解释"程序是否满足线程内语义"意味着什么?是否有可能提供满足这些语义并且不满足这些语义的程序的简单示例?
前两个例子:
1)
MyClass myClass;
for (int i=0; i<arrayList.size(); i++) {
myClass = arrayList.get(i);
...
}
2)
for (int i=0; i<arrayList.size(); i++) {
MyClass myClass = arrayList.get(i);
...
}
Run Code Online (Sandbox Code Playgroud)
在第一个示例中,引用变量myClass仅创建一次.但是在第二个例子中,它是仅创建一次,还是每次迭代创建一次?我的想法也许是编译器优化了这个,我不知道.
我试图通过编写一个例子回答这个问题,但无法弄明白.如何通过代码证明?
注意:我意识到示例2是更好的样式,因为myClass在for循环之外是未知的,并且它的范围保持最小.我也在这里搜索过,但是没有找到这个确切问题的确定答案(通常这是一个"哪个是首选的?"的问题.)我还假设如果每次迭代都创建了myClass引用,那么它不是一个大的表现问题.
编辑:再说一遍,我不是问哪种编码风格更好.此外,我想知道它是否可以通过代码推断/证明.我试图生成并比较字节码,但我不熟悉字节码,生成的内容不完全匹配.
我有一个非常特别的问题,我找不到答案。
正如我们所知,在synchronized块的入口处,线程会重新读取其范围内的所有共享(非本地)变量。某些底层架构的示例:如果线程 A 更新 RAM 中的对象状态,线程 B 进入同步块将看到更改。类似的事情发生在退出synchronized块时:线程将其范围内的所有内容刷新到 RAM,以便其他线程可以看到它。这些是基本的 JVM 可见性保证,并且存在发生之前的规则来强制执行它。
但是,从语义上讲,代码使用wait()或notify()也执行所有这些操作并不是很清楚:毕竟它没有明确地进入或离开synchronized块。
这些问题是:
wait()入口处对其他线程的更改可见?wait()休假时所做更改的可见性?notify()?java multithreading synchronized java-memory-model happens-before
我有一个要求,我需要在完成后发布“n”个线程的结果。为了检查所有线程是否已完成,我使用了 AtomicInteger (incrementAndGet()) 并将其值与最终变量进行比较。在进行检查之前,我将各个线程的结果写入共享对象(写入并发散列图,因为非同步数据结构 dint 似乎就足够了)。所以,我的问题是,在我的计数器通过“if”条件之前,所有线程是否会完成对共享对象的写入(并且主线程是否能够看到一致的内存)?
这是示例代码:
公共无效运行(){
//Values is a concurrent hashMap.
values.put(Thread.currentThread().getName(), Thread.currentThread().getName());
if(counter.incrementAndGet() == 5) {
//Do something
}
}
Run Code Online (Sandbox Code Playgroud) 我正在尝试看看volatile这里的工作方式。如果声明cc为volatile,则输出如下。我知道线程执行输出有时会有所不同,但是我读到的volatile内容与相同synchronized,那么为什么要获得此输出?如果我使用两个实例,Thread1那有关系吗?
2线程-0
2线程1
4线程1
3线程-0
5线程1
6线程-0
7线程1
8线程-0
9线程1
10线程-0
11线程1
12线程-0
public class Volexample {
int cc=0;
public static void main(String[] args) {
Volexample ve=new Volexample();
CountClass count =ve.new CountClass();
Thread1 t1=ve.new Thread1(count);
Thread2 t2=ve.new Thread2(count);
t1.start();
t2.start();
}
class Thread1 extends Thread{
CountClass count =new CountClass();
Thread1(CountClass count ){
this.count=count;
}
@Override
public void run() {
/*for(int i=0;i<=5;i++)
count.countUp();*/
for(int i=0;i<=5;i++){
cc++;
System.out.println(cc + Thread.currentThread().getName()); …Run Code Online (Sandbox Code Playgroud) 想象一下下面的程序。
class Main {
static class Whatever {
int x = 0;
}
public static void main(String[] args) {
Whatever whatever = new Whatever();
Thread t = new Thread(() -> {
whatever.x = 1;
});
t.start();
try {
t.join();
}
catch (InterruptedException e) {
}
System.out.println(whatever.x);
}
}
Run Code Online (Sandbox Code Playgroud)
主线程已缓存whatever并x设置为0。另一个线程启动、缓存whatever并将缓存设置x为1.
输出是
1
Run Code Online (Sandbox Code Playgroud)
所以主线程已经看到了写入。这是为什么?
为什么写入共享缓存,为什么主线程使其缓存失效以从共享缓存读取?为什么我不需要volatile这里?
请帮助我找到以下两个问题的答案,均在java面试中问到:
在这种情况下,将使用new关键字在JVM堆栈中分配内存(堆中没有任何内容)。
在这种情况下,方法重载将基于返回类型,方法名称和参数相同(我回答说在Java中是不可能的)
根据我的知识和从Google的发现,两者都无法完成,我的答案是:
New将始终在堆中分配内存,并且可以通过堆栈中的引用进行引用
重载取决于编译时间,如果不遵循以下情况,它将给编译器错误
但是他没有被说服。