java并发编程笔记(五)——线程安全策略

时间:2021-12-20 13:43:31

java并发编程笔记(五)——线程安全策略

不可变得对象

不可变对象需要满足的条件

  • 对象创建以后其状态就不能修改
  • 对象所有的域都是final类型
  • 对象是正确创建的(在对象创建期间,this引用没有逸出)

final关键字:类、方法、变量

  • 修饰类:不能被继承
  • 修饰方法:1、锁定方法不被继承类修改;2、效率,目前近期版本已经将私有方法默认设置为final,final能够使方法转为内嵌调用。
  • 修饰变量:基本数据类型变量、引用类型变量

定义不可变对象的其他方法:

  • Collections.unmodifiablexxx:Collection、List、Set、Map... //这种方法直接禁止对集合进行任何操作,否则会报错。
public class ImmutableExample2 {

    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); //抛异常
log.info("{}", map.get(1));
} }
  • Guava:Immutablexxx:Collection、List、Set、Map...

    如果改变集合,则会抛异常

    public class ImmutableExample3 {
    
        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);
    
        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) {
    System.out.println(map2.get(3));
    }
    }

线程封闭

1、Ad-hoc线程封闭:程序控制实现,最糟糕,忽略

2、堆栈封闭:通过设置局部变量,无并发问题

3、ThreadLocl线程封闭:特别好的封闭方法

正产情况下,每一个请求对服务器来说,都是一个单独的线程。

如果不适用,ThreadLock,下层代码想要获取上层代码的某些值,需要将值不端的往下传,造成代码臃肿。

在使用ThreadLocal的时候,注意要删除里边的东西,否则会造成内存泄漏,因为ThreadLock里边的东西只有在重启服务器的时候会清空。

应用:jdbc的Connection

线程不安全类与写法

不安全的类

StringBuffer与StringBuilder

stringBuffer虽然线程安全,但是内部使用了synchronize关键字,性能有损耗

如何本身是单线程环境或者不会涉及线程安全问题,还是以StringBuilder为主

SimpleDataFormat与DataTimeFormatter

SimpleDataFormat不能轻易作为全局变量在多线程环境下使用,它是线程不安全的;

DataTimeFormatter不是JDK自带的,需要额外引入一个jar包(joda-time)

在实际使用中:推荐使用DataTimeFormatter

ArrayList、HashSet|、HashMap等Collections

ArrayList:线程不安全

HashSet:线程不安全

HashMap:线程不安全

不安全的写法

先检查再执行:if(condition(a){handle(a)})

当两个线程同时执行到if语块的时候,可能会发生并发情况,造成handle重复执行,这种情况下应考虑加锁。

线程安全的同步容器(性能不太好)

  • ArrayList -> Vector(不一定是完全线程安全)、Stack
  • HashMap -> HashTable(key,value不能为null)
  • Collections.synchronizedXXX(List,Set,Map)

线程安全的并发容器J.U.C

J.u.c(java.util.concurrent)

1、ArrayList -> CopyOnWriteArrayList

CopyOnWriteArrayList:写操作的时候,复制,当有新元素添加到list中的时候,它先从原有的数组里边拷贝一份出来,然后在新的数组上进行写操作,然后再将原来的数组指向新的数组。

整个的add操作都是在锁的保护下进行的,主要是为了避免在add进行复制数组的时候,复制出多个副本导致数据混乱。

缺点:

  • 在写操作的时候需要复制数组,所以比较消耗内存,如果元素的内容比较多,可能会导致yong GC和full GC
  • 不能用于实时读的场景,因为集合拷贝数组,新增元素都需要时间,可保证数据安全,但无法保证实时性,更适合读多写少的场景

​ 如果在使用的时候无法知道所存储数据量的大小,还是要慎用。

该集合的设计思想:

  • 读写分离;
  • 最终一致性;
  • 使用时另外开辟空间,通过这种方式能够避免并发冲突。

2、HashSet、TreeSet -> CopyOnWriteArraySet、ConcurrentSkipListSet

CopyOnWriteArraySet(对应HashSet)

这个集合是线程安全的,底层用了上述的CopyOnWriteArrayList,一次这个集合也是适合数据大小比较小的set集合,所以写操作的开销也很大。

ConcurrentSkipListSet(对应TreeSet)

支持自然排序,支持在构造的时候自定义比较器,基于Map集合,可变操作都是现成安全的。但是对于批量操作比如addAll(),removeAll()不能支持原子操作,不支持空元素。

3、HashMap、TreeMap->ConcurrentHashMap、ConcurrentSkipListMap

ConcurrentHashMap不允许空值

ConcurrentSkipListMap

key是有序的,支持更高的并发,

JUC总结

java并发编程笔记(五)——线程安全策略

  • 线程限制:一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改
  • 共享制度:一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问,但是任何线程都不能修改它。
  • 线程安全对象:一个线程安全的对象或者容器,在内部通过同步机制来保证线程安全,所以其他线程无需额外的同步就可以通过公共接口随意访问它。
  • 被守护对象,被守护对象只能通过获取特定的锁来访问

java并发编程笔记(五)——线程安全策略的更多相关文章

  1. java并发编程笔记(七)——线程池

    java并发编程笔记(七)--线程池 new Thread弊端 每次new Thread新建对象,性能差 线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM 缺 ...

  2. java并发编程笔记(三)——线程安全性

    java并发编程笔记(三)--线程安全性 线程安全性: ​ 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现 ...

  3. &lbrack;Java并发编程(五)&rsqb; Java volatile 的实现原理

    [Java并发编程(五)] Java volatile 的实现原理 简介 在多线程并发编程中 synchronized 和 volatile 都扮演着重要的角色,volatile 是轻量级的 sync ...

  4. java并发编程笔记(十)——HashMap与ConcurrentHashMap

    java并发编程笔记(十)--HashMap与ConcurrentHashMap HashMap参数 有两个参数影响他的性能 初始容量(默认为16) 加载因子(默认是0.75) HashMap寻址方式 ...

  5. java并发编程笔记(九)——多线程并发最佳实践

    java并发编程笔记(九)--多线程并发最佳实践 使用本地变量 使用不可变类 最小化锁的作用域范围 使用线程池Executor,而不是直接new Thread执行 宁可使用同步也不要使用线程的wait ...

  6. java并发编程笔记(八)——死锁

    java并发编程笔记(八)--死锁 死锁发生的必要条件 互斥条件 进程对分配到的资源进行排他性的使用,即在一段时间内只能由一个进程使用,如果有其他进程在请求,只能等待. 请求和保持条件 进程已经保持了 ...

  7. java并发编程笔记(六)——AQS

    java并发编程笔记(六)--AQS 使用了Node实现FIFO(first in first out)队列,可以用于构建锁或者其他同步装置的基础框架 利用了一个int类型表示状态 使用方法是继承 子 ...

  8. java并发编程笔记(四)——安全发布对象

    java并发编程笔记(四)--安全发布对象 发布对象 使一个对象能够被当前范围之外的代码所使用 对象逸出 一种错误的发布.当一个对象还没构造完成时,就使它被其他线程所见 不安全的发布对象 某一个类的构 ...

  9. java并发编程笔记(二)——并发工具

    java并发编程笔记(二)--并发工具 工具: Postman:http请求模拟工具 Apache Bench(AB):Apache附带的工具,测试网站性能 JMeter:Apache组织开发的压力测 ...

随机推荐

  1. AIX扩展文件系统的大小

    由于AIX系统空间不够需要增加硬盘,希望增加文件系统的空间,折腾了好几天怎么都不能扩展文件系统的空间,原来是把硬盘加错了卷组 首先,确定文件系统所在的LV /dev/datalv      270.0 ...

  2. Mysql 创建数据库表(删除,删除,插入)

    MySQL 创建数据表 创建MySQL数据表需要以下信息: 表名 表字段名 定义每个表字段 语法 以下为创建MySQL数据表的SQL通用语法: CREATE TABLE table_name (col ...

  3. MemoryMappedFile 内存映射文件 msdn

    http://msdn.microsoft.com/zh-cn/library/dd997372%28v=vs.110%29.aspx 内存映射文件 .NET Framework 4.5 其他版本 1 ...

  4. AngularJS学习篇(十四)

    AngularJS 事件 ng-click 指令 ng-click 指令定义了 AngularJS 点击事件. <!DOCTYPE html> <html> <head& ...

  5. Android drawText 做到文字绝对居中

    我们在android中经常会遇到自定义一些组件,因为现有的android组件是往往不能满足当下的需求的,今天就给大家介绍一下在自定义组建过程中用到的drawText不居中的问题的解决方案 首先大家看一 ...

  6. 0412ooday01&period;txt&equals;&equals;&equals;&equals;&equals;&equals;&equals;&equals;&equals;&equals;&equals;&equals;&equals;对象和类(上)

    对象和类(上) 面向对象程序设计:面向过程的结构化程序设计.什么是抽象数据类型.什么是类 定义一个类:定义类的成员变量.定义类的方法 创建并使用对象:使用new关键字创建对象.引用类型变量.访问对象的 ...

  7. 历届试题 小数第n位 (求循环节)

    只要被除数出现重复,就表明循环节出现了.即使商不是循环小数,也可以补0作为循环节,这样就可以统一处理了. AC代码 #include <stdio.h> #include <vect ...

  8. C&num; — 动态获取本地IP地址及可用端口

    1.在VS中动态获取本地IP地址,代码如下: 2.获取本机的可用端口以及已使用的端口:

  9. DevExpress 之 GridControl 自定义列

    Ø  前言 DevExpress 控件大家应该都有所了解,使用这个框架实现B/S或C/S的,都是非常出色的.本文主要讨论下 GridControl 中如何[自定义列]或[计算列],可使用以下两种方法实 ...

  10. 使用laravel搭建CURD后台页面

    配置即一切 一切皆于需求,后台从0开始搭建,但是写了一两个页面后发现太多的是对单表的增删改查操作,于是就想到了,能不能做一个快速搭建的后台.想到一句话,配置即一切.如果一个CURD后台能只进行配置就自 ...