线程安全和不可变性(Thread Safety and Immutability)

本文深入探讨了不可变对象如何确保线程安全,解释了构造方法、getter方法、setter方法的使用,并通过例子展示了如何利用不可变对象达到线程安全。同时,文章强调了不可变对象的引用可能不具有线程安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文链接:http://tutorials.jenkov.com/java-concurrency/thread-safety-and-immutability.html

竞争条件只会在多个线程访问相同的资源并且一个或多个线程改写这个资源的情况下发生。如果多个线程只是同时读取相同的资源不会导致竞争条件的发生。

我们可以通过某种手段让共享的对象不可改变,从而可以确保这些不可改变的共享对象不会被任何一个线程改变,因此这些不可改变的对象是线程安全的。下面是一个例子:

    public class ImmutableValue {
        private int value = 0;
        public Immutablevalue(int value) {
            this.value = value;
        }
        public int getValue() {
            return this.value;
        }

    }

注意,ImmutableValue 类的实例的值是通过构造方法传递的、 ImmutableValue 类是没有 set 方法的,一旦 ImmutableValue 类的实例被创建以后就不能改变它的值。 ImmutableValue 类的实例是不可改变的,可以通过 getValue() 方法来读取它的值。

如果需要在这个 ImmutableValue 类的实例上执行操作,可以把不可变对象原有的值结合需要的操作产生一个新的值,然后用这个新的值新建一个实例,最后把这个新建的实例返回。下面是一个 add 操作的例子:

    public class ImmutableValue {
        private int value = 0;

        public ImmutableValue(int value) {
            this.value = value; 
        }

        public int getValue() {
            return this.value;
        }


            public ImmutableValue add(int valueToAdd) {
                return new ImmutableValue(this.value + valueToAdd);
            }

    }

注意, add() 方法用 add 操作的结果重新构造了一个 ImmutableValue 类的实例并返回,而不是直接对原来的不可变对象的值 value 做 add 操作。

引用不是线程安全的!

记住,即使一个对象是不可变的因此这个对象是线程安全的,但这个对象的引用未必是线程安全的。看下面的例子:

    public class Calculator {
        private ImmutableValue currentValue = null;

        public ImmutableValue getValue() {
            return currentValue;
        }

        public void setValue(ImmutableValue newValue) {
            this.currentValue = newValue;
        }

        public void add(int newValue) {
            this.currentValue = this.currentValue.add(newValue);
        }
    }

Calculator 类持有 ImmutableValue 类的实例的一个引用。注意,可以通过 setValue() 和 add() 方法来改变这个引用指向的对象。因此,即使 Calculator 类在内部使用了一个不可变的对象,但并不是说这个 Calculator 类就不可改变,它还是线程不安全的。换句话说, ImmutableValue 类是线程安全的,但使用了这个类的类未必也是线程安全的。这是通过不可变性来达到线程安全时需要注意的问题。

为了使 Calculator 类达到线程安全,可以在 getValue()、 setValue() 和 add() 方法前声明为 synchronized 方法。声明为 synchronized 方法消除了这个问题。

Next: Java 内存模型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值