以下内容装载于
慕课网《Java并发编程与高并发解决方案》
《深入理解java虚拟机》第二版,386页~387页
1.什么是不可变对象?
不可变对象一定是线程安全的,因为无论是方法的实现者还是调用者,都不需要采用任何的线程安全保护措施;不可变对象一经创建以后他的状态就不能被改变,对象的所有域都是final类型的;对象是正确创建的(在创建对象的期间没有发生,this引用逸出)。
2.声明不可变对象的方式有哪些?
2.1使用final修饰
使用final关键字修饰,我们应该都不陌生。
- 修饰类:该类不能被继承,该类所有的方法都不能被重写(常见的final类有String,基本数据类型的包装类,BigInteger,BigDecimal)
- 修饰方法:方法不能被继承类重写
- 修饰变量:一个final变量实质上时是一个常量(当一个变量修饰基本数据类型的时候,变量的值不可以改变;当用final修饰引用类型的时候,此时变量不可以指向别的地址,但是注意可以修改该引用里面的值。也正因为这样,当该引用变量为全局变量的时候,就可能会引发线程安全问题)
注:final修饰变量的时候赋值时机:
final修饰成员变量
- 在声明变量的等号右边直接赋值;
- 在构造函数中赋值;
- 在类的初始代码块中赋值。
final修饰全局静态变量
- 在声明变量的等号右边直接赋值;
- 在static代码中赋值,但是不能在普通代码块汇总赋值。
final修饰局部变量
- 在声明变量的等号右边直接赋值;
- 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));
}
}