我有一组关于 Java 多线程问题的问题。请尽可能多地帮助我。
0) 假设我们有 2 个银行账户,我们需要以线程安全的方式在它们之间转账。 即
accountA.money += transferSum;
accountB.money -= transferSum;
存在两个要求:
- 任何人都不应该能够看到操作的中间结果(即一个账户的总和增加了,但其他人还没有减少)
- 在操作期间不应阻止读取访问(即在操作进行期间应显示帐户金额的旧值)
你能就此提出一些想法吗?
1) 假设 2 个线程通过同步方法或使用显式锁修改某个类字段。无论同步如何,都不能保证该字段对通过 NOT 同步方法读取它的线程可见。 - 是否正确?
2) 被notify方法唤醒的线程可以等待锁多长时间?假设我们有这样的代码:
synchronized(lock) {
lock.notifyall();
//do some very-very long activity
lock.wait() //or the end of synchronized block
}
我们可以声明至少有一个线程会成功并获取锁吗?信号会因超时而丢失吗?
3) 引自 Java Concurrency Book:
“单线程执行器还提供足够的内部同步,以保证任务进行的任何内存写入对后续任务可见;这意味着对象可以安全地限制在“任务线程”中,即使该线程可能被替换不时另一个。”
这是否意味着在单线程执行器中执行的代码唯一剩下的线程安全问题是数据竞争,我们可以放弃 volatile 变量并忽略所有可见性问题?它看起来像是解决大部分并发问题的通用方法。
4) 所有标准的 getter 和 setter 都是原子的。如果该字段被标记为易变的,则它们不需要同步。 - 是否正确?
5) 静态字段和静态 block 的初始化由一个线程完成,无需同步。 - 是否正确?
6)为什么一个线程通过wait()方法离开锁需要通知其他人,而通过退出synchronized block 离开锁则不需要通知其他人?
最佳答案
0:你不能。
确保原子更新很容易:您可以在持有银行账户的任何对象上进行同步。但是你要么阻止所有读者(因为他们也同步),要么你不能保证读者会看到什么。
但是,在银行系统等大型系统中,锁定频繁访问的对象并不是一个好主意,因为它会在系统中引入等待。在更改两个值的特定情况下,这可能不是问题:它会发生得如此之快,以至于大多数访问都是无竞争的。
当然有办法避免这种竞争条件。数据库在银行账户方面做得很好(尽管最终它们依赖于对交易结束的竞争访问)。
1) 据我所知,除了由 synchronized
或 volatile
建立的保证外,没有其他保证。如果一个线程进行同步访问而另一个线程不进行同步访问,则非同步访问没有内存屏障。 (如果我错了,我相信我会得到纠正或至少被否决)
2) 引用 JavaDoc:“被唤醒的线程将无法继续,直到当前线程放弃对该对象的锁定。”如果您决定让 sleep 进入那个同步块(synchronized block),您会不高兴的。
3) 我必须多次阅读该引用才能确定,但我相信“单线程执行程序”是关键词。如果执行者只运行一个线程,那么该线程上的所有操作都存在严格的先行关系。它并不意味着在其他执行器中运行的其他线程可以忽略同步。
4) No. long
和 double
不是原子的(参见 JVM spec )。如果您想要对成员变量进行非同步访问,请使用 AtomicXXX
对象。
5) 不。我无法在 JVM 规范中找到确切的引用,但是 2.17.5 部分意味着多个线程可以初始化类。
6) 因为所有 线程等待直到一个 线程执行通知。如果您在一个同步块(synchronized block)中,并等待它并且没有通知,那么每个线程都将等待一个永远不会发生的通知。
关于java - 关于java多线程的一些问题,,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6789477/