仅使用不可变数据类型是否使Java程序线程安全?

仅使用不可变数据类型是否使Java程序线程安全?,java,multithreading,thread-safety,immutability,Java,Multithreading,Thread Safety,Immutability,如果我只使用不可变数据类型,我的Java程序将是线程安全的,这是真的吗 是否有其他因素会影响螺纹安全性 ****如果能提供一个例子,我将不胜感激。谢谢** **线程安全是关于保护共享数据和不可变对象的,因为它们是只读的。除了创建它们之外,创建对象是线程安全的 值得一提的是,设计一个只使用不可变对象来实现线程安全的大型应用程序是很困难的 这是一个复杂的课题,我建议你阅读 这是一个很好的起点。的确如此。问题是,应用程序只使用不可变的数据类型是一个非常严重的限制。不能有任何状态为的持久对象跨线程存在

如果我只使用不可变数据类型,我的Java程序将是线程安全的,这是真的吗

是否有其他因素会影响螺纹安全性

****如果能提供一个例子,我将不胜感激。谢谢**
**线程安全是关于保护共享数据和不可变对象的,因为它们是只读的。除了创建它们之外,创建对象是线程安全的

值得一提的是,设计一个只使用不可变对象来实现线程安全的大型应用程序是很困难的

这是一个复杂的课题,我建议你阅读
这是一个很好的起点。

的确如此。问题是,应用程序只使用不可变的数据类型是一个非常严重的限制。不能有任何状态为的持久对象跨线程存在

我不明白你为什么要这么做,但这并不意味着它不真实


详细信息和示例:

如果每个变量都是不可变的(一旦赋值就永远不会更改),那么您的程序确实是线程安全的

环境利用了这一点

然而,要用一种从一开始就不是为它设计的语言进行纯函数式编程是相当困难的

在纯函数程序中不能执行的一个简单示例是使用循环,因为不能增加计数器。您必须使用递归函数来实现相同的效果


如果您刚刚步入线程安全和并发的世界,我衷心推荐Goetz的书。它是为Java编写的,但实际上它所讨论的问题在其他语言中也是相关的,即使这些问题的解决方案可能不同。

不变性允许针对多线程情况下可能出现的某些问题提供安全性。具体来说,这意味着在第一个线程使用对象时,一个线程可见的对象的属性不能被另一个线程更改(因为没有任何东西可以更改它,那么显然另一个线程不能)

当然,这只适用于对象移动的范围。如果对对象的可变引用也被共享,那么通过将新对象放在其中(但不是全部,因为线程是否在已被替换的对象上工作可能无关紧要,但这可能是至关重要的),可能会发生一些跨线程错误


总之,不变性应该被视为确保线程安全的方法之一,但这不是唯一的方法,也不一定足够。

尽管不变性对象有助于线程安全,但您可能会发现“局部变量”和“同步”对于现实世界的编程更实用。

如果程序状态的可变方面没有被多个线程访问,那么任何程序都将是三线程安全的,因为每个线程都可能是自己的独立程序。然而,有用的多线程通常需要线程之间的交互,这意味着存在一些可变的共享状态

安全高效的多线程的关键是在正确的“设计级别”上结合可变性。理想情况下,程序状态的每个方面都应该由一个不可变根(*)表示,该不可变根(*)是对一个对象的可变引用,该对象的可观察状态是不可变的。一次只有一个线程可以尝试更改特定可变引用表示的状态。高效的多线程要求程序状态中的“可变层”足够低,不同的线程可以使用它的不同部分。例如,如果一个线程有一个不可变的
AllCustomers
数据结构,并且两个线程同时尝试更改不同的客户,那么每个线程都会生成一个包含自身更改的
AllCustomers
数据结构版本,但不包括另一个线程的更改。无益。但是,如果
AllCustomers
CustomerState
对象的可变数组,则一个线程可能处理
AllCustomers[4]
,而另一个线程处理
AllCustomers[9]
,而不受干扰


(*)当状态方面变得相关时,根路径必须存在,并且在访问相关时不得更改。例如,可以设计一个名为
Arr
AddOnlyList
,其中包含一个名为
Arr
thing[]
并初始化为大小32。添加第一个对象时,
Arr[0]
将使用
compareeexchange
初始化为一个包含16个
对象的数组。接下来的15件事情将在该阵列中进行。添加第17项时,
Arr[1]
将使用
compareeexchange
对大小为32的数组进行初始化(该数组将保存新项及其后的31项)。当添加第49项时,
Arr[2]
将为64项初始化。请注意,虽然
thing
本身及其包含的数组不是完全不变的,但只有对任何元素的第一次访问才是写入,并且一旦
Arr[x][y]
持有对某个元素的引用,只要
Arr
存在,它就会继续这样做。

@Erick Robertson-一个解决所有问题的例子!:)@willcodejavaforfood-如果您能提供该示例,将不胜感激。谢谢@埃里克:如果他能成功,那可能值得所有的食物。这本身并不难,但对FP有一个坚实的理解是必要的,使用函数式语言可以减少痛苦。事实上,以线程不安全的方式创建对象是可能的。从构造函数启动线程是破坏线程安全性的好方法,例如,如果新线程引用了您尚未完成构造的对象。@Bill Michel-是的,Java并发在实践中涉及到了这一点。你必须意识到