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

时间:2010-09-22 14:55:47

标签: java multithreading thread-safety immutability

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

任何其他因素都会影响线程的安全性吗?

****如果能提供一个例子,我将不胜感激。谢谢!** **

6 个答案:

答案 0 :(得分:7)

线程安全是关于保护共享数据和不可变对象受到保护,因为它们是只读的。除了创建它们之外,创建对象是线程安全的。

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

这是一个复杂的主题,我建议你阅读Java Concurrency in Practice 这是一个非常好的起点。

答案 1 :(得分:3)

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

我不明白你为什么要这样做,但这并没有让它变得那么真实。

详细信息和示例:http://www.javapractices.com/topic/TopicAction.do?Id=29

答案 2 :(得分:2)

如果每个变量都是不可变的(一旦分配就不会改变),你确实会有一个简单的线程安全程序。

Functional programming环境利用了这一点。

但是,用一种不是从头开始设计的语言进行纯函数式编程是非常困难的。

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

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

答案 3 :(得分:0)

不可变性允许对多线程案例可能出错的某些事情进行安全保护。具体来说,这意味着当第一个线程正在使用它时,另一个线程无法更改一个线程可见的对象的属性(因为没有可以更改它,那么显然另一个线程不能)。

当然,这只适用于该对象。如果对象的可变引用也是共享的,那么跨线程错误的一些情况可能发生在那里放置一个新对象(但不是全部,因为如果一个线程在已经被替换的对象上工作可能无关紧要,但那可能是至关重要的。)

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

答案 4 :(得分:0)

虽然不可变对象是线程安全的帮助,但您可能会发现“局部变量”和“同步”对于现实世界的编程更加实用。

答案 5 :(得分:0)

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

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

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