Java 线程安全

线程安全性?

当多个线程访问某一个类,不管运行时环境采用何种调度方式,或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步
或协同,这个类都能够表现出正确的行为。那么这个类就是线程安全的类。

原子性:提供了互斥访问,同一时刻只能有一个线程来对它进行操作。 
可见性:一个线程对主内存的修改可以及时的被其他线程观察到。
有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序。

LongAdder 和 AtomicLong 区别?

[https://blog.csdn.net/yao123long/article/details/63683991]

锁 互斥

synchronized : 依赖java虚拟机
    不可中断的锁,适合竞争不激烈,可读性好。
Lock :jdk 通过特殊的Cpu指令,代码实现,ReentranLock
    可以中断的锁,多样化同步,竞争激烈时能维持常态。
Atomic:
竞争激励的时能维持常态,比Lock性能好,只能同步一个值    

synchronized 同步锁

修改代码块 :大括号括起来的代码,作用于调用的对象
修饰方法   :整个方法,作用于调用的对象
修饰静态方法 :整个静态方法,作用于所有对象
修饰一个类  : 括号括起来的部分,作用于所有对象

可见性

导致共享变量在线程间不可见的原因?

线程交叉执行
重排序结合线程交叉执行
共享变量更新后的值没有在工作内存与主存间及时更新

JMM 关于synchronized的两条规定:

线程解锁前,必须把共享变量的最新值刷新到主内存中。
线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁和解锁是
同一把锁)

volatile

对于可见性,Java提供了volatile关键字来保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

通过加入内存屏障和禁止重排序优化来实现:
   对volatile变量写操作时,会在写操作后加入一条store屏障指令,将本地内存中的共享变量值刷新到主内存中去。
   对volatile变量读操作时,会在读操作前加入一条load屏障指令,从主内存中读取共享变量。

有序性

java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,但是却会影响到多线程的
并发执行的正确性。

主要包含: volatile 、 synchrionized 、lock 
先天的有序性

Happens before 原则(深入理解java虚拟机)

1)程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先发生于书写在后面的操作。
2)锁定规则 :一个unlock操作先行发生于后面对同一个锁的lock操作
3)volatile 变量规则:对一个变量的写操作先发生于后面对这个变量的读操作
4)传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出结论,操作A先行发生于操作B
5)线程启动原则:Thread对象的start()方法先行发生于次线程的每一个动作 
6)线程中断规则:对线程Interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
7)线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.jion()方法结束、
Thread.isAive()的返回值手段检测到线程已经终止执行
8)对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法开始。

除了这八条规则,虚拟机就可以随意对他们进行重排序。

总结

原子性: Atomic包、CAS算法、synchronized 、lock
同一时间内只有一个线程可以对其进行访问和修改。

可见性:synchronized、 volatile
一个线程对主内存的修改可以及时的被其他线程知道。
    
有序性: happens - before

不可变对象

不可变对象满足的条件(参考String):

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

将类定义成final 不可以集成
将成员都变成私有的,不可以直接访问这些成员
对变量不提供set和get方法
将所有可以变的成员生命为final,这样通过构造器初始化所有成员,进行深度拷贝

安全发布对象 (这一节的第一章没有搞清楚)

发布对象:使一个对象能够被当前范围之外的代码所使用。

对象逸出:一种错误的发布。当一个对象还没有构造完成时,就使它被其他线程所见。

安全发布对象的四种方法?

1)在静态的初始化函数中初始化一个对象引用
2)将对象的引用保存到volatile类型域或者AtomicReference 对象中
3)将对象的引用保存到某一个正确的构造对象的final类型域中
4)将对象引用保存到一个由锁保护的域中

线程封闭

把一个对象封闭到一个线程里边,只允许这个对象在这个线程里进行访问。

Ad-hoc 线程封闭:程序控制实现,最糟糕,可以忽略
堆栈封闭:局部变量,并无并发问题
TreadLocal 线程封闭:特别好的方法(本质是维护了一个Map,key是线程名称,value对应线程对象)

线程不安全的写法

线程不安全       线程安全
StringBuilder   StringBuffer
SimpleDateFormat  jodaDateTimeFormatter
ArrayList、HashMap、HashSet 等Collections 容器均是线程不安全的

同步容器

ArrayList -> Vector (矢量)、Stack(栈)
HashMap   -> HashTable(key,value 不能为空)
Collections.synchronizedXXX(List、Set、Map) //集合 排序 处理


同步容器场景越来越少
1)synchronized 会降低性能
2)并不能完全保证线程安全

并发容器 (J U C)

ArrayList -> CopyOnWriteArrayList

原理: copy一个副本,进行写操作,写操作加锁。
缺点
1、需要copy数组,就要消耗内存,如果元素比价多的时候,可能导致 gc
2、不能应用于实时读的场景

适合:读多 写少的操作 

设计思想:
1、读写分离 
2、最终一致性
3、使用时另外开辟控件
4、读不加锁 写是加锁的(防止多个线程copy出多个副本)

HashSet、TreeSet-> CopyOnWriteArraySet (CopyOnWriteArrayList)、ConcurrentSkipListSet(jdk1.6)

CopyOnWriteArraySet:
线程安全
1、copy开销较大
2、不支持remove操作
3、遍历速度非常快,适合读操作大于写操作的情况

ConcurrentSkipListSet:
支持自然排序、定义比较细
1、Map
2、add 、put 、move 线程安全的
3、不允许使用空元素Null
4、addAll RoveAll  等是批量线程不安全的

HashMap、 TreeMap -> ConcurrentHashMap 、ConcurrentSkipListMap

ConcurrentHashMap:
1、不允许空值
2、具有特别高的并发性

ConcurrentSkipListMap:

1、SkipList 跳表 
2、key 是有序  支持更高的并发

安全共享对象策略

1)线程限制:一个被线程限制的对象,由线程独占,并且只能被占有他的线程修改

2)共享只读:一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问,但是任何
线程都不能修改它

3)线程安全对象:一个线程安全的对象或容器,在内部通过同步机制来保证线程安全,所以其他的线程无需额外的同步
就可以通过公共接口随意访问它

4)被守护对象:被守护对象只能通过获取特定的锁来访问

并发测试工具有哪一些?

PostMan : Http请求模拟工具
Apache Bench (AB) :Apache附带的工具,测试网站性能 (小巧灵活、命令行操作)
JMeter : Apache组织开发的压力测试工具 (图形界面话操作)
(这个仔细的去学习一下)

补充知识点

注意,为了获得较好的执行性能,Java内存模型并没有限制执行引擎使用处理器的寄存器或者高速缓存来提升指令执行速度,也没有限制编译器对指令进行重排序。
也就是说,在java内存模型中,也会存在缓存一致性问题和指令重排序的问题。   
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容

  • 浅谈java内存模型 不同的平台,内存模型是不一样的,但是jvm的内存模型规范是统一的。其实java的多线程并发问...
    流浪java阅读 388评论 1 3
  • 本系列出于AWeiLoveAndroid的分享,在此感谢,再结合自身经验查漏补缺,完善答案。以成系统。 Java基...
    济公大将阅读 1,527评论 1 6
  • 第2章 java并发机制的底层实现原理 Java中所使用的并发机制依赖于JVM的实现和CPU的指令。 2.1 vo...
    kennethan阅读 1,417评论 0 2
  • 本文参与#漫步青春#征文活动,作者 李欣然,本人承诺,内容为原创,且未在其他平台发布 ...
    lxr3033阅读 107评论 0 0
  • 有一天,一个教室的学生都去上体育课了。于是教室里的橡皮都出来开会。 有一个粉色的橡皮说“看我多漂亮呀...
    祥颐阅读 469评论 0 0