[笔记]Java多线程基础——线程和线程安全

因为处理器主频在硬件发展上的瓶颈,摩尔定律基本失效,现在真正起作用的是并行处理的Amdahl定律,毕竟,现在计算机的瓶颈在于存储和通信,而不是运算本身,并行运算可以更充分地发挥运算的能力,也是提升计算机性能。

线程及其实现

进程
进程是操作系统进行资源分配调度的最小单位,各进程有独立的系统资源(如内存、文件I/O等),互相之间不能直接访问,很多时候,一个进程就是一个应用,多个进程也许可以并发,但是进程本身没有并发的概念,需要借助线程实现并发。
线程
线程是处理器调度和分配的最小单位,线程没有自己的资源,线程既能共享所在进程的系统资源(线程私有的局部变量表来自于主内存,只为线程本身服务,输出的结果仍然要写回主内存),又能相对独立地执行调度。
线程因此可以通过并行处理提升效率,但也因此会互相干扰,如果线程崩溃,就会影响所在进程的所有线程。
状态
线程有5种状态
New,创建后未启动。
Runnable,Ready或Running,正等待CPU或正在执行。
Waiting/Timed Waiting,无限等待需要其他线程唤醒,限期等待会由系统自动唤醒
sleep是Native方法,由操作系统来阻塞线程,指定时间后恢复线程。
无限等待会调用wait,需要另一个线程调用notify()来通知恢复线程。
Blocked,阻塞,线程在等待锁被另一个线程释放出来
Terminated,线程已结束
这五种状态的转换关系如下:

线程状态转换关系

实现
Java用Thread实现线程,每个已经start的Thread都是一个线程,需要注意的是,Thread都是Native实现的,也就是通过操作系统,而不是JVM支持的。
在操作系统上,实现线程主要有三种方式:
1.内核线程
内核线程KLT是直接在操作系统内核上支持的,KLT和CPU之间只隔着一个调度器Thread Schedular,调度器把线程中的任务直接映射到各个CPU上。
程序一般会通过轻量级进程来使用内核线程,这是内核线程的高级接口,每个内核线程对应一个轻量级进程。
内核线程的所有操作都要在系统中调用,需要频繁在用户态和内核态切换,代价很高。
2.用户线程
用户线程完全在用户进程中创建,不需要切换内核态,所以开销很小,但是因为没有系统支持,进程之间的协调、阻塞、处理器映射等,都需要自己实现,所以非常复杂。
3.用户线程+轻量级进程
其实就是把前两种模式混合使用。

线程安全

线程安全问题其实就是并发的正确性问题,一个线程安全的行为,既不需要额外的同步和协调,也不用考虑在runtime中的调度和交替执行,一定能返回预期的结果。
五种线程安全场景
1.不变性
最简单最纯粹的场景就是不变性,一个不可变的对象一定是线程安全的,如final。
2.绝对线程安全
绝对线程安全是不切实际的,即使是线程安全的Vector容器,也只是在方法中用了synchronized修饰,方法调用时还是需要额外同步,否则,在多线程同时remove,仍然会有Index边界溢出的错误。
3.相对线程安全
一般意义上的线程安全就是相对线程安全,单独操作是线程安全的,但是在特定情况下,还需要在调用时增加额外的同步手段。Java提供的线程安全如Vector、HashTabe、Collections.synchronizedCollection()等,都是相对线程安全。
4.线程兼容
一般意义上的不是线程安全其实是线程兼容,指的是本身并不线程安全,可以在调用时增加同步手段,实现线程安全,常见的ArrayList和HashMap都是线程安全的。
5.线程对立
一些极端情况下,无论采用什么同步措施,都不能实现线程安全,就是线程对立,如Thread的suspend和resume,不能并行调用,很容易出现死锁。
实现线程安全,既与代码的编写有关,也与虚拟机的同步和锁有关,常见的三种线程安全实现方法为:
1.互斥同步
就是共享数据在并行运算中,同一时刻只能一个线程使用,synchronized和ReentrantLock都是互斥同步。
2.非阻塞同步
其实就是互斥同步的对立面,非阻塞同步相对乐观,认为并行不一定导致共享数据冲突,如果真的出现争用冲突,再做补偿即可(如重试操作,比如compareAndSet(current,next)就是不断尝试赋值,如果current和next的值和预期不一致,就说明数据被修改了,会再次循环尝试),sum.misc.Unsafe类就是非阻塞同步机制(ClassLoader才能直接使用,用户只能通过Java API间接使用,如AtomicInteger),非阻塞同步依赖于硬件指令集的发展和支持。
3.无同步方案
无同步方案不是不管线程安全,而是通过其他方式实现线程安全,不需要同步。
可重入代码
一个方向是通过代码实现无同步,就是可重入代码,可重入代码在执行过程中,随时可以中断,转而执行其他任务(包括递归该代码本身),然后重入继续执行,不会出现错误。
可重入代码也叫纯代码,容易令人想起纯函数(当然,不是同一维度),只要输入相同的数据,就能返回相同的结果。
线程本地存储
另一个方向是通过避免多线程的数据共享实现无同步,就是线程本地存储,也就是把共享数据控制在一个线程内,避免冲突。
大部分使用消费队列的模式都是线程本地存储,这种模式会尽量在一个线程内完成消费,Android中的Handler机制,就是通过ThreadLocal对象(实际上是一个HashMap,key为对象的hashcode,value为对象本身),让handler引用线程的Looper,Looper再依次处理自己MessageQueue中的Message,通过Message的target指向handler,实现在同一线程内处理消息队列。

锁的优化

多线程的重点是数据的高效共享,主要得解决竞争的问题,也就是对锁的优化。
自旋锁与自适应自旋
自旋针对的是线程的阻塞和恢复,因为线程的阻塞和恢复非常消耗资源,而等待的锁可能很快就会释放,所以在线程请求锁失败的时候,不立即阻塞线程,而是让它先执行一个忙循环(自旋)。
自旋也会消耗资源,适当的自旋次数效果才最好,自适应自旋会根据以往的自旋次数,动态调整自旋次数,基本策略就是自旋后能获得锁,下次就可以多自旋几次;如果自旋后没有获得锁,下次就会少自旋几次。
锁消除
在编译代码时,编译器认为某个锁完全没有必要,就会把锁消除。
锁粗化
如果虚拟机发现一串零碎的操作中,对同一个对象反复加锁解锁,就会把它们合并扩展为外侧的一个锁。
轻量级锁
绝大部分锁在同步周期是没有竞争的,加锁和解锁的操作虽然必须,但是消耗过重了,轻量级锁就是先用轻量级的锁来加锁解锁,如果同步期间没有发生竞争,就节省了资源;如果发生了竞争,就膨胀为常规的重量级的锁。
偏向锁
偏向锁和轻量级锁都是针对无竞争情况的优化,轻量级锁是在无竞争时消除互斥,而偏向锁是在无竞争时消除整个同步。

逃逸的优化

根据对象的作用域,可能发生逃逸行为,如果可以确认不会发生逃逸,也能进行优化。
方法逃逸
在方法中定义个一个对象,如果可能被外部方法引用,比如作为外部方法的参数,就是方法逃逸。
如果不会发生方法逃逸,就可以在线程的栈上分配内存,这样,在栈帧出栈时就可以回收内存,减轻GC压力。
线程逃逸
在线程中的一个对象,如果可能被外部线程引用,比如赋值给静态类变量或者其他线程的对象变量,就是线程逃逸。
如果不会发生线程逃逸,就可以消除同步,消除对变量的同步措施,因为在线程内部是天然有序的,不存在竞争问题。

引用

《深入理解Java虚拟机》
synchronized(this)与synchronized(class)
Java集合及concurrent并发包总结(转)
《深入理解Java虚拟机》读书笔记7:高效并发
深入理解java虚拟机 精华总结(面试)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容