对不起,这是一个很长的问题。
我最近在多线程中进行了大量研究,因为我慢慢将其应用到个人项目中。然而,可能由于存在大量略微不正确的例子,在某些情况下使用同步块和波动对我来说仍然有点不清楚。
我的核心问题是:当一个线程在同步块内部时,对引用和原语的更改是否自动易失(即,在主内存而不是缓存上执行),或者读取是否也必须同步才能使其工作正常吗?
- 如果是这样 同步简单的getter方法的目的是什么? (见例1) 此外,只要线程已同步到任何内容,是否所有更改都发送到主内存?例如,如果它被发送到一个非常高级别同步的地方做大量的工作,那么每次改变都会发生在主存储器中,并且没有任何东西可以缓存,直到它再次被解锁?
- 如果不 更改是否必须显式位于同步块内,或者java实际上是否可以接受,例如,使用Lock对象? (见例3)
- 如果是 同步对象是否需要与以任何方式更改的引用/原语相关(例如,包含它的直接对象)?我可以通过同步一个对象来写,如果它安全吗,可以用另一个对象阅读吗? (见例2)
(请注意以下示例,我知道同步方法和synchronized(this)是不赞成的,为什么,但讨论超出了我的问题的范围)
例1:
class Counter{
int count = 0;
public synchronized void increment(){
count++;
}
public int getCount(){
return count;
}
}
在此示例中,increment()需要同步,因为++不是原子操作。因此,同时递增的两个线程可能导致计数总体上增加1。 count原语需要是原子的(例如,不是long / double / reference),并且它很好。
getCount()需要在这里同步吗?为什么呢?我听到的最多的解释是,我不能保证返回的计数是增量前还是后增量。然而,这似乎是对某些略有不同的解释,那就是错误的地方。我的意思是,如果我要同步getCount(),那么我仍然看不到保证 - 它现在归结为不知道锁定顺序,不知道实际读取是在实际写入之前/之后。
例2:
以下示例线程是否安全,如果您假设通过此处未显示的技巧,这些方法中的任何一个都不会同时被调用?如果每次都使用随机方法完成,那么计数会以预期的方式递增,然后被正确读取,或者进行锁定 有 成为同一个对象? (顺便说一下,我完全意识到这个例子有多么荒谬,但我对理论比对实践更感兴趣)
class Counter{
private final Object lock1 = new Object();
private final Object lock2 = new Object();
private final Object lock3 = new Object();
int count = 0;
public void increment1(){
synchronized(lock1){
count++;
}
}
public void increment2(){
synchronized(lock2){
count++;
}
}
public int getCount(){
synchronized(lock3){
return count;
}
}
}
例3:
之前发生的关系只是一个java概念,还是内置于JVM中的实际内容?即使我可以保证下一个例子的概念发生 - 之前的关系,如果它是一个内置的东西,java是否足够聪明地接受它?我假设它不是,但这个例子实际上是线程安全的吗?如果它的线程安全,那么如果getCount()没有锁定呢?
class Counter{
private final Lock lock = new Lock();
int count = 0;
public void increment(){
lock.lock();
count++;
lock.unlock();
}
public int getCount(){
lock.lock();
int count = this.count;
lock.unlock();
return count;
}
}