线程安全之不可变对象

以下内容装载于
慕课网《Java并发编程与高并发解决方案》
《深入理解java虚拟机》第二版,386页~387页

1.什么是不可变对象?

不可变对象一定是线程安全的,因为无论是方法的实现者还是调用者,都不需要采用任何的线程安全保护措施;不可变对象一经创建以后他的状态就不能被改变,对象的所有域都是final类型的;对象是正确创建的(在创建对象的期间没有发生,this引用逸出)。

2.声明不可变对象的方式有哪些?

2.1使用final修饰

使用final关键字修饰,我们应该都不陌生。

  1. 修饰类:该类不能被继承,该类所有的方法都不能被重写(常见的final类有String,基本数据类型的包装类,BigInteger,BigDecimal)
  2. 修饰方法:方法不能被继承类重写
  3. 修饰变量:一个final变量实质上时是一个常量(当一个变量修饰基本数据类型的时候,变量的值不可以改变;当用final修饰引用类型的时候,此时变量不可以指向别的地址,但是注意可以修改该引用里面的值。也正因为这样,当该引用变量为全局变量的时候,就可能会引发线程安全问题)

注:final修饰变量的时候赋值时机:
final修饰成员变量

  1. 在声明变量的等号右边直接赋值;
  2. 在构造函数中赋值;
  3. 在类的初始代码块中赋值。

final修饰全局静态变量

  1. 在声明变量的等号右边直接赋值;
  2. 在static代码中赋值,但是不能在普通代码块汇总赋值。

final修饰局部变量

  1. 在声明变量的等号右边直接赋值;
  2. final修饰局部变量的时候,不指定赋值时机,只是要求在使用前必须赋值(和普通的成员变量用法一致);

示例代码:final修饰基础数据类型和引用类型的变量的区别。

package com.mark.example.immutable;

import com.google.common.collect.Maps;
import com.mark.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

@Slf4j
@NotThreadSafe
public class ImmutableTest1 {

    private final static Integer a = 1;
    private final static String b = "2";
    private final static Map<Integer, Integer> map = Maps.newHashMap();

    static {
        map.put(1, 2);
        map.put(3, 4);
        map.put(5, 6);
    }

    public static void main(String[] args) {
//        a = 2; final修饰的基础数据类型不可以改变
//        b = "3";
//        map = Maps.newHashMap(); //final修饰的引用类型变量,引用类型不可以指向新的地址
        map.put(1, 3);//但是注意可以修改引用类型里的值【引发线程不安全】
        log.info("{}", map.get(1));
    }

    private void test(final int a) {//final也可以用来修饰局部变量
//        a = 1; //那么在局部变量的值也是不可以修改的
    }
}

总结:修饰基础数据类型后,变量的值不可以修改;当final修饰引用类型的时候,变量执行执行的地址不可以修改,但是可以修改变量的属性值。

2.2使用 Collections.unmodifiableXXX

示例代码:使用JDK自带的Collections类声明不可变的对象

**package com.mark.example.immutable;

import com.google.common.collect.Maps;
import com.mark.annotations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.Map;

/**
 * author:Mark
 * date:2018/7/31  8:09
 * 线程安全不可变对象: Collections.unmodifiableXXXX
 */
@Slf4j
@ThreadSafe
public class ImmutableTest2 {
    private static Map<Integer, Integer> map = Maps.newHashMap();

    static {
        map.put(1, 2);
        map.put(3, 4);
        map.put(5, 6);
        map = Collections.unmodifiableMap(map);
    }

    public static void main(String[] args) {
        map.put(1, 3); //抛出异常,不可以改变final引用变量中的数据
        log.info("{}", map.get(1));
    }
}

2.3使用ImmutableXXX

实例代码:使用第三方(guava)包,声明不可变对象

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>23.0</version>
</dependency>
package com.mark.example.immutable;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.mark.annotations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.Map;

/**
 * author:Mark
 * date:2018/7/31  8:09
 * ImmutableXXX(com.google.common.collect)第三方包Guava
 */
@Slf4j
@ThreadSafe
public class ImmutableTest3 {

    private final static ImmutableList<Integer> list = ImmutableList.of(1, 2, 3);

    private final static ImmutableSet set = ImmutableSet.copyOf(list);

    private final static ImmutableMap<Integer, Integer> map = ImmutableMap.of(1, 2, 3, 4);//key-value,key-value

    private final static ImmutableMap<Integer, Integer> map2 = ImmutableMap.<Integer, Integer>builder()
            .put(1, 2).put(3, 4).put(5, 6).build();


    public static void main(String[] args) {
        //list.add(4);//抛出异常
       // set.add(4);//抛出异常
        System.out.println(map2.get(3));
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章