Java 这段代码如何保证线程安全?
我在看一本书 及Java 这段代码如何保证线程安全?,java,thread-safety,Java,Thread Safety,我在看一本书 及 我有一个问题,如何才能说前一个代码是线程安全的,而后一个不是。如果两个线程使用相同的UserBuilder,如下所示: User.UserBuilder builder = new User.UserBuilder("Jhon", "Doe"); // First thread Thread t1 = new Thread(){ @Override public void run() { builder.age(
我有一个问题,如何才能说前一个代码是线程安全的,而后一个不是。如果两个线程使用相同的UserBuilder,如下所示:
User.UserBuilder builder = new User.UserBuilder("Jhon", "Doe");
// First thread
Thread t1 = new Thread(){
@Override
public void run()
{
builder.age(30).build();
}};
// Second thread
Thread t2 = new Thread(){
@Override
public void run()
{
// Just changing age temporarily in builder.
builder.age(140);
builder.age(35).build();
}};
t1.start();
t2.start();
使用build()
的线程安全实现,您将永远不会有年龄大于120岁的用户,因为将引发非法状态异常
在第二种情况下,当t1检查条件时,
age==30
但当新用户(此)
被称为age==140
。在这种情况下,你不会得到任何异常,关于年龄的不变量被打破。这在第一种情况下不会发生。正如@AshwineeKJha所观察到的,该帖子解释了“线程安全”的含义,即新用户对象的年龄属性是否一定会按预期进行验证。由于该属性基于final
字段,因此在对象完全构造后读取该值的线程肯定会看到该属性的最终值。在构造之后验证新对象,而不是在构造之前验证生成器,从而避免了一种类型的线程安全问题
另一方面,如果构建器对象在线程之间共享,则一个线程写入其age
字段,另一个线程读取其age
字段,则需要在两个线程之间进行同步。在没有同步的情况下可以发生这样的事件组合的程序是不正确同步的。对于类/方法,“线程安全”的通常定义是,当实例在类之间共享时,类的用户不需要执行任何外部同步来确保正确的同步。从这个意义上讲,两个代码都不是线程安全的。关于线程安全的评论使事情变得不清楚
本文似乎暗示了在多个线程之间共享UserBuilder
的相同实例(builder
)的方向。无论如何,这不是一个好主意。如中所述,构建器模式的主要用途是在创建不可变类的一致实例时避免伸缩构造函数,该模式仔细编码并演示了该模式的使用
请注意,User
(如果final
类)是不可变的(因此是线程安全的),但是UserBuilder
不是(因此是线程不安全的)。这是故意的。其思想是通过作为其生成器的线程不安全类来创建User
类的一致实例。因此,在使用构建器之前,您必须小心,并且可能对在线程之间共享它们心存疑虑
如果您必须在多个线程之间共享一个构建器,那么这里提出的建议可能是相关的。这项建议是以局部变量的堆栈限制为基础的。变量user
是仅由创建它的线程看到的局部变量。这使得这种使用线程安全。第二个实现巧妙地调用了UserBuilder
本身的线程不安全特性,并引发了问题
通常,最好只在build
方法中返回User
的一个新实例,并在生成器上提供的setters
中保留单独的检查。是的,在线程之间明智地共享生成器实例。请参阅有效的Java项目2。如果age
是当前类的成员,那么getAge()
看起来像什么?@svasa请参考链接中的帖子,它可以编译build
是一种实例方法。局部变量始终是线程安全的。它取决于构造函数的功能以及类中可用的其他函数。
public User build() {
if (age > 120) {
throw new IllegalStateException(“Age out of range”); // bad, not thread-safe
}
// This is the window of opportunity for a second thread to modify the value of age
return new User(this);
}
User.UserBuilder builder = new User.UserBuilder("Jhon", "Doe");
// First thread
Thread t1 = new Thread(){
@Override
public void run()
{
builder.age(30).build();
}};
// Second thread
Thread t2 = new Thread(){
@Override
public void run()
{
// Just changing age temporarily in builder.
builder.age(140);
builder.age(35).build();
}};
t1.start();
t2.start();