摘要:通过指令重排可以减少流水线停顿提升巨大效率原则程序顺序原则一个线程内保证语义的串行性。锁规则解锁必然发生在随后的加锁前。线程的方法先于它的每一个动作。
1. 基本概念
同步(Synchronous)和异步(Asynchronous) 并发(Conncurrency)和并行(Parallelism) 临界区 阻塞(Blocking)与非阻塞(Non-Blocking) 死锁(Deadlock)、饥饿(Starvation)和活锁(Livelock)
同步(Synchronous)和异步(Asynchronous)
并发(Conncurrency)和并行(Parallelism)
临界区
临界区:公共资源或共享数据,可被多个线程使用,但每次只能有一个线程使用,其他线程需等待
阻塞(Blocking)与非阻塞(Non-Blocking)
阻塞:一个线程占用了临界区资源,其他线程就会在就必须在临界区等待,等待会导致线程挂起,这就是阻塞
非阻塞:没有一个线程可以妨碍其他线程执行,所有线程都会不断尝试向前
死锁(Deadlock)、饥饿(Starvation)和活锁(Livelock)
死锁:A线程中占用a资源,并且尝试去获取b资源,同时B线程中占用着b资源,并且尝试去获取a资源,此时谁也无法进行下去,导致死锁
饥饿:资源竞争激烈时,某个线程长时间无法获取到资源,产生饥饿
活锁:两个线程竞争同一资源时相互谦让,导致两个线程一直在谦让而无法正常工作
阻塞 无饥饿(Starvation-Free) 无障碍(Obstruction-Free) 无锁(Lock-Free) 无等待(Wait-Free)
阻塞
使用synchronized关键字或重入锁等时,会阻塞其他线程获取临界区资源
无饥饿(Starvation-Free)
使用公平锁时,先到先得,不会产生饥饿;非公平锁,由于竞争激烈,或者某些线程优先级高导致低优先级的线程有可能产生饥饿
无障碍(Obstruction-Free)
无障碍是最弱的非阻塞调度,两个线程均可修改临界区数据,一旦检测到数据不安全,即对自己修改的数据进行回滚,确保数据安全,冲突严重时所有线程会不停回滚从而造成系统无法正常工作.
一般会配合"一致性标记"一起使用,操作前读取一致性标记,修改后再次读取此标记,若不一致则表示数据不安全
无锁(Lock-Free)
无锁的并行都是无障碍的.无锁的并发必然有一个线程能够在有限步内完成操作离开临界区.一般都会包含无穷循环,且可能产生饥饿
无等待(Wait-Free)
无等待在无所的基础上更进一步,要求所有线程必须在有限步内访问完临界区,这样就不会引起饥饿问题
读线程都是无等待的,写数据时,采用RCU(Read Copy Update)策略,先取得原数据副本,再修改副本数据,修改完后在合适的机会写回数据
JMM的技术点围绕多线程的原子性、可见性和有序性来建立的
原子性(Atomicity)
32位虚拟机中操作long型变量是非原子性的,可能造成数据不安全
public class MultiThreadLong { public volatile static long t=0; public static class ChangeT implements Runnable{ private long to; public ChangeT(long to){ this.to=to; } @Override public void run() { while(true){ MultiThreadLong.t=to; //赋值临界区的t Thread.yield(); //让出资源 } } } public static class ReadT implements Runnable{ @Override public void run() { while(true){ long tmp=MultiThreadLong.t; if(tmp!=111L && tmp!=-999L && tmp!=333L && tmp!=-444L) System.out.println(tmp); //打印非正常值 Thread.yield(); //让出资源 } } } public static void main(String[] args) { new Thread(new ChangeT(111L)).start(); new Thread(new ChangeT(-999L)).start(); new Thread(new ChangeT(333L)).start(); new Thread(new ChangeT(-444L)).start(); new Thread(new ReadT()).start(); //输出: //-4294966963 //4294966852 //-4294966963 } }
可见性
可见性是指一个线程修改某一个共享变量的值时,其他线程是否能够立即知道这个修改
原因:缓存优化或者硬件优化问题(内存读写不会立即触发,而先进入一个硬件队列);指令重排和编辑器的优化
有序性
并发时,程序的执行可能出现乱序,给人感觉就是:写在前面的代码,会在后面执行
public class OrderExample { int a=0; boolean flag=false; public void writer(){ a=1; flag=true; //这一步不一定在a=1之后 } public void reader(){ if(flag){ int i=a+1; //当flag=true时,a不一定为1,也可能未执行a=0 } } }
为什么要指令重拍呢? 完全是出于性能考虑,一条指令可以分为以下几步: .取指IF。 ·译码和取寄存器操作数ID。 ·执行或者有效地址计算EX。 ·存储器访问MEM。 ·写回WB。 通过指令重排可以减少cpu流水线停顿,提升巨大效率 Happen-Before原则: ·程序顺序原则:一个线程内保证语义的串行性。 ·volatile规则:volatile变量的写先于读发生,这保证了volatile变量的可见性。 ·锁规则:解锁(unlock)必然发生在随后的加锁(lock)前。 ·传递性:A先于B,B先于C,那么A必然先于C。 .线程的start()方法先于它的每一个动作。 ·线程的所有操作先于线程的终结(Thread.join())。 ·线程的中断(interrupt())先于被中断线程的代码。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/75926.html
摘要:探究系统登录验证码的实现后端掘金验证码生成类手把手教程后端博客系统第一章掘金转眼间时间就从月份到现在的十一月份了。提供了与标准不同的工作方式我的后端书架后端掘金我的后端书架月前本书架主要针对后端开发与架构。 Spring Boot干货系列总纲 | 掘金技术征文 - 掘金原本地址:Spring Boot干货系列总纲博客地址:http://tengj.top/ 前言 博主16年认识Spin...
摘要:实战读书笔记第一章从方法传递到接着上次的,继续来了解一下,如果继续简化代码。去掉并且生成的数字是万,所消耗的时间循序流并行流至于为什么有时候并行流效率比循序流还低,这个以后的文章会解释。 《Java8实战》-读书笔记第一章(02) 从方法传递到Lambda 接着上次的Predicate,继续来了解一下,如果继续简化代码。 把方法作为值来传递虽然很有用,但是要是有很多类似与isHeavy...
阅读 1264·2021-09-23 11:51
阅读 1368·2021-09-04 16:45
阅读 626·2019-08-30 15:54
阅读 2074·2019-08-30 15:52
阅读 1592·2019-08-30 11:17
阅读 3098·2019-08-29 13:59
阅读 2010·2019-08-28 18:09
阅读 380·2019-08-26 12:15