并发编程-10线程安全策略之不可变对象
文章目录
- 脑图
- 四个线程安全策略
- 不可变对象定义
- 不可变对象需要满足的条件
- 如何创建不可变对象
- 使用final关键字定义不可变对象
- 修饰变量示例
- final修饰基本数据类型及String: 初始化之后不能修改 (线程安全)
- final修饰引用类型变量:初始化之后不能再修改其引用,但可以修改值 (线程不安全)
- 使用JDK / Guava中提供的工具类创建不可变对象
- Collections.unmodifiableXXX 示例 (线程安全)
- Guava ImmutableXXX 示例 (线程安全)
- 代码
脑图
四个线程安全策略
线程限制
一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改
共享只读
一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问,但是任何线程都不能修改它
线程安全对象
一个线程安全的对象或者容器,在内部通过同步机制来保证线程安全,所以其他线程无需额外的同步就可以通过公共接口随意访问它
被守护对象
被守护对象只能通过获取特定的锁来访问
不可变对象定义
在Java中,有一种对象发布了就是安全的,被称之为不可变对象。
不可变对象可以在多线程中可以保证线程安全
不可变对象需要满足的条件
- 对象创建以后其状态就不能修改
- 对象所有域都是final类型
- 对象是正确创建的(在对象创建期间,this引用没有逸出)
如何创建不可变对象
将类声明成final类型,使其不可以被继承
将所有的成员设置成私有的,使其他的类和对象不能直接访问这些成员
对变量不提供set方法
将所有可变的成员声明为final,这样只能对他们赋值一次
通过构造器初始化所有成员,进行深度拷贝
在get方法中,不直接返回对象本身,而是克隆对象,返回对象的拷贝
提到不可变的对象就不得不说一下final关键字,该关键字可以修饰类、方法、变量:
使用final关键字定义不可变对象
final关键字可以修饰类、方法、变量
- 修饰类:不能被继承(final类中的所有方法都会被隐式的声明为final方法)
- 修饰方法:
1、锁定方法不被继承类修改;
2、可提升效率(private方法被隐式修饰为final方法)
- 修饰变量:
基本数据类型变量: 初始化之后不能修改
引用类型变量: 初始化之后不能再修改其引用
- 修饰方法参数:同修饰变量
修饰变量示例
final修饰基本数据类型及String: 初始化之后不能修改 (线程安全)
可知:编译报错,被final修饰后,基本类型和String的变量无法被修改
final修饰引用类型变量:初始化之后不能再修改其引用,但可以修改值 (线程不安全)
package com.artisan.example.immutable;import java.util.ArrayList;
import java.util.List;
import java.util.Map;import com.google.common.collect.Maps;import lombok.extern.slf4j.Slf4j;@Slf4j
public class FinalDemo {// 基本数据类型 int 使用final修饰 验证被final修饰的基本数据类型无法改变private final static int num = 1;// String类型 使用final修饰 验证被final修饰的基本数据类型无法改变private final static String name = "小工匠";// 引用类型 初始化之后不能再修改其引用,但是可修改其中值private final static Map<String, Object> map = Maps.newHashMap();static {map.put("name", "artisan");map.put("age", 20);map.put("sex", "男");}public static void main(String[] args) {// 被final修饰的基本数据类型和String无法改变// 编译报错: The final field FinalDemo.num cannot be assigned// num = 2;// 编译报错: The final field FinalDemo.name cannot be assigned// name = "artisan";// 引用对象,此引用无法指向别的对象,但可修改该对象的值map.put("name", "小工匠");log.info("name:{}", map.get("name"));// 验证 方法参数被final修饰的情况List<String> list = new ArrayList<>();list.add("我是小工匠");test2(list);}// final修饰传递进来的变量基本类型,不可别改变private void test(final int a) {// 不能修改// 编译报错: The final local variable a cannot be assigned. It must be blank and not using a compound assignment// a = 2;log.info("a:{}", a);}// final修饰方法传递进来的变量 引用对象,无法指向别的对象,但可修改该对象的值private static void test2(final List<String> list) {// 添加数据list.add("我是artisan");list.forEach(str ->{log.info("数据:{}",str);});}}
输出:
需要我们注意的是,final修饰引用类型时,虽然不能将引用再指向别的对象,但可修改该对象的值。 线程不安全
使用JDK / Guava中提供的工具类创建不可变对象
除了final可以定义不可变对象,java提供的Collections类,也可定义不可变对象。
JDK中的 Collections.unmodifiableXXX传入的对象一经初始化便无法修改,XXX可表示Collection、List、Set、Map等
谷歌的Guava中的ImmutableXXX,XXX同样可表示Collection、List、Set、Map等
Collections.unmodifiableXXX 示例 (线程安全)
执行后结果如下:
由此可见,用Collections.UnmodifiableMap修饰的对象是不可修改的,如果尝试修改对象的值,在程序运行时会抛出异常。
跟下Collections.UnmodifiableMap的源码
继续看下UnmodifiableMap
主要是将一个新的集合的所有更新方法变为抛出异常
Guava ImmutableXXX 示例 (线程安全)
package com.artisan.example.immutable;import com.artisan.anno.ThreadSafe;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;@ThreadSafe
public class GuavaImmutableSetDemo {// 使用Guava中提供的类来定义不可变对象的集合// 不可变listprivate final static ImmutableList<Integer> list = ImmutableList.of(1, 2, 3);// 不可变的setprivate final static ImmutableSet<Integer> set = ImmutableSet.copyOf(list);// 不可变的map,需要以k/v的形式传入数据,即奇数位参数为key,偶数位参数为valueprivate final static ImmutableMap<String, String> map = ImmutableMap.of("k1", "v1", "k2","v2");// 通过builder调用链的方式构造不可变的mapprivate final static ImmutableMap<String, String> map2 = ImmutableMap.<String, String>builder().put("key1", "value1").put("key2", "value2").put("key3", "value3").build();public static void main(String[] args) {// 修改对象内的数据就会抛出UnsupportedOperationException异常// 不能添加新的元素 ,运行将抛出 java.lang.UnsupportedOperationExceptionlist.add(4);// 不能添加新的元素 ,运行将抛出 java.lang.UnsupportedOperationExceptionset.add(4);// 不能添加新的元素 ,运行将抛出 java.lang.UnsupportedOperationExceptionmap.put("k3", "v3");// 不能添加新的元素 ,运行将抛出 java.lang.UnsupportedOperationExceptionmap2.put("key4", "value4");}
}
上述代码是线程安全的,开发时如果我们的对象可以变为不可变对象,我们尽量将对象变为不可变对象,这样可以避免线程安全问题。
代码
https://github.com/yangshangwei/ConcurrencyMaster
并发编程-10线程安全策略之不可变对象相关推荐
- 并发编程-11线程安全策略之线程封闭
文章目录 脑图 概述 线程封闭的三种方式 示例 堆栈封闭 ThreadLocal Step1. ThreadLocal操作类 Step2. 自定义过滤器 Step3. 注册拦截器,配置拦截规则 Ste ...
- 并发编程-13线程安全策略之两种类型的同步容器
文章目录 脑图 概述 同步容器 集合接口下的同步容器实现类 Vector (线程安全性比ArrayList好一些,但并非绝对线程安全) 同步容器 线程不安全的场景 其他注意事项 Hashtable C ...
- 并发编程-12线程安全策略之常见的线程不安全类
文章目录 脑图 概述 字符串拼接子之StringBuilder.StringBuffer StringBuilder (线程不安全) StringBuffer (线程安全) 小结 时间相关的类 Sim ...
- java并发编程实战wwj----------------------第二阶段-------不可变对象-------19-20
不可变对象一定是线程安全的. 可变对象不一定是安全的,因为里面会加锁. ------------------------------------------------ servlet不是线程安全的, ...
- 并发编程-14线程安全策略之并发容器(J.U.C)中的集合类
文章目录 J.U.C总览 脑图 概述 并发容器特性 示例 ArrayList对应的线程安全的并发容器类CopyOnWriteArrayList (线程安全) HashSet对应的线程安全的并发容器类C ...
- 【Java 并发编程】线程池机制 ( ThreadPoolExecutor 线程池构造参数分析 | 核心线程数 | 最大线程数 | 非核心线程存活时间 | 任务阻塞队列 )
文章目录 前言 一.ThreadPoolExecutor 构造参数 二.newCachedThreadPool 参数分析 三.newFixedThreadPool 参数分析 四.newSingleTh ...
- 【Java 并发编程】线程池机制 ( 线程池示例 | newCachedThreadPool | newFixedThreadPool | newSingleThreadExecutor )
文章目录 前言 一.线程池示例 二.newCachedThreadPool 线程池示例 三.newFixedThreadPool 线程池示例 三.newSingleThreadExecutor 线程池 ...
- 并发编程-05线程安全性之原子性【锁之synchronized】
文章目录 线程安全性文章索引 脑图 概述 原子性synchronized 修饰的4种对象 修饰代码块 作用范围及作用对象 Demo 多线程下 同一对象的调用 多线程下不同对象的调用 修饰方法 作用范围 ...
- java并发编程与线程安全
2019独角兽企业重金招聘Python工程师标准>>> 什么是线程安全 如果对象的状态变量(对象的实例域.静态域)具有可变性,那么当该对象被多个线程共享时就的考虑线程安全性的问题,否 ...
最新文章
- eclipse或者myeclipse的代码提示功能
- 帷幕的帷是什么意思_俗语:“宁娶寡妇,不娶生妻!”什么是“生妻”?老祖宗智慧...
- 自定义的 ListBoxItem 自适应ListBox的宽度
- 线性规划实战—投资的收益和风险
- PHP生成日历(实例详解)
- vs最好的版本_Win10 环境下,LightGBM GPU 版本的安装
- 同期两篇 Nature:运行温度高于 1K 的量子计算平台问世!
- VMware快照功能与(非永久)永久磁盘详解
- dede文章异步ajax加载,织梦DedeCMS列表“加载更多”“无限下拉”Ajax加载使用方法...
- 语义分割Swin Transformer
- kernel打印模块驱动加载时间
- 【等保小知识】等保二级是否需要做密评?什么是密评?
- python arduino 微信_MicroPython动手做(27)——物联网之微信小程序
- 解决opencv imwrite()影像全黑
- 【低碳发展案例研究】中国西部中小城市低碳发展研究——以泸州市为例
- win在哪打开java_win7系统打开java控制面板的操作方法
- python 携程航班信息查询
- 知名的兴趣社群平台小打卡是如何获得5000万用户的?【黑盒研究内参第11期】...
- 【堆】leetcode378.有序矩阵中第K小的元素
- 计算机自考考研学校有哪些专业,适合自考生考研的学校有哪些