聊聊并发处理和java线程
2017-12-20 12:49
246 查看
并发处理
并发处理的引入为了加速,是为了“压榨”计算机的运算能力,例如CPU运算能力比其他存储、通讯子系统的速度快太多。Amdahl(阿姆达尔定律):
S=1/(1-a+a/n)
S:固定负载情况下描述并行处理效果的加速比
a:为并行计算部分所占比例;
n:为并行处理结点个数;
当a=0时(即只有串行,没有并行),最小加速比s=1。
当1-a=0时,(即没有串行,只有并行)最大加速比s=n,当n→∞时,极限加速比s→ 1/(1-a),这也就是加速比的上限。
例如,若串行代码占整个代码的25%,则并行处理的总体性能不可能超过4
如果要拿这个算程序处理用几个线程,还要考虑线程切换本身的消耗。
曲线参考
加速不仅仅靠并发
加速的最终目标是为了让计算机帮我们处理跟多的“任务”,而每个“任务”,不是仅靠“运算”就能完成,至少要和内存交换数据。引入读写速度接近处理器的高速缓冲
对输入代码进行乱序(out-of-order execution)执行但是保障执行结果不变,充分利用运算单远()
上面两个策略也带来2个问题
缓存一致性(处理高速缓冲和共享内存的数据一致性)
指令重排序(执行顺序跟代码顺序不一定一致)
java内存模型
主要目标是定义程序各个变量的访问规则,虚拟机中将变量存储到内存和从内存中取出的底层细节(不包括线程私有的,局部变量、方法参数)。线程 工作内存 | 操作 | 主内存 |
---|---|---|
java线程1<---->工作内存1 | (sava/load) | 主内存(共享) |
java线程2<---->工作内存2 | (sava/load) | 主内存(共享) |
一定要关联主内存堆(物理:内存),工作内存虚拟机栈中(物理:提前放到寄存器和高速缓存)
内存交互操作
定义了8种操作:lock:作用于主内存变量,标记被一个线程独占,引发清空工作内存此变量值,用前重新load,assign。
unlock:作用于主内存变量,把锁定的变量释放出来,unlock前必须执行store,write写回主内存。
read:作用于主内存变量,主内存传输到线程工作内存,以便后面load操作。
load:作用于工作内存变量,把read得到的变量值,放入工作内存的变量副本中。
use:作用于工作内存变量,把变量值传给执行引擎。
assign:作用于工作内存变量,从执行引擎收到的值赋值给工作内存变量。
store:作用于工作内存变量,包工作内存的值传到主内存,以便随后的write操作。
write:作用于主内存变量,包store拿到的值放入到主内存变量中。
java虚拟只保障顺序的执行read和load(store和write),但是不保证连线执行。可能中间执行了其他指令。
但是对上面8种基本操作加了一些规则:
变量只能在主内存“诞生”
对变量“lock”,必须先清空此变量的工作内存值,在执行引擎执行的时候,重新load。
其他的就是,不能无缘无顾中间开始或结束,不多讲。
volatile变量访问规则
“可见性”:一个线程改变volatiel变量值,另外一个线程使用时可以立即可见,由于每次使用前先刷新,所以对执行引擎来说看起来是一致的。但是运算不一定是原子性的,所以运算在并发下不一定安全(真实是存在不一致的)。“禁止指令重排序”:添加内存屏障,内存屏障之后对指令不能放到内存屏障之前执行。
volatile bool initailized=false; // initialize xxxx initailized=true;//会添加内存屏障,保证判断变量时之前(initialize)代码全部执行完。 if(initailized){ //do something }
volatile使用场景:
运算结果不依赖变量当前值(如:i++),或者保证只有一个线程改变变量值。
变量不需要与其他的状态变量共同参与不变约束。
long 和dubbo变量
“long/dubbo非原则性协定”:内存模型要求“lock...write"这8个操作原子性,但是对于没有被volatile修饰的64位的数据类型划分成2次操作,不保证原子性。(一般商用虚拟机还是保证原子性的)先行发生原则(happens-before)
两项操作直接的偏序关系,A操作先于B操作,就代表A操作产生的影响,B操作可见。program order rule
monitor lock rule
volatile varible rule
thread start rule
thread termination rule
thread interruption rule
finalize rule
transitivity
java线程
内核:操作系统的最基础部分。内核线程(KLT:kernel-level thread):操作系统内核支持的线程,内核调度scheduler调度线程,将线程任务映射到处理器。
轻量级线程(LWP:light weight process):操作内核线程的高级接口。
用户线程(UT:user thread):线程都在用户态实现,内核不知道用户线程的存在。
实现方式:
使用内核线程实现:使用了内核线程优势,但是切换成本高。
使用用户线程实现:没有内核线程支持,受限制
使用混合实现:UT---LWP,多对多
java线程调度
cooperative(协作,线程自己控制时间) 和preemptive(抢占,系统分配执行时间) Thread-scheduling线程状态
newrunnable
waiting
timed waiting
blocked
terminated
线程安全
一个对象被多线程同时使用,依然可以得到正确的结果,那么这个对象是安全的。不可变:final、不可变类型:String、枚举类型、java.lang.Number的子类
绝对线程安全与相对线程安全:如:vector:ArrayIdnexOutOfBoundsException
线程兼容:可以正确的使用,打到线程安全
线程对立:怎么用都是有风险的,比如thread.suspend和resume
线程安全实现方法
相关概念梳理:Blocking Synchronize:基于线程阻塞和唤醒实现。问题就是线程切换的成本问题。
Non-Blocking Synchronize:乐观并发策略,先进行操作,再根据检查是否有其他线程竞争共享数据,如没有成功,有失败。
互斥同步
保证同一个时间,只有一个线程访问,互斥是方法,同步是目的,同步实现上一些特性如下:可重入:同步块,同一线程可一重复进入,防止自己锁自己的死锁。
等待可中断:正在等待锁的线程可以选择放弃等待。
乐观并发策略
依赖硬件指令的原子性实现,比如常见指令(熟悉redis的会觉得眼熟):Test-and-Set
Fecth-And-Increment
Swap
Compare-and-swap
Load-Linked/Store-Conditional
当然也可以自己实现,比如循环重试,成功为止。
其他实现
避开共享数据和公用资源,自然线程安全。线程本地存储,避开公共资源争用。
锁优化
JDK1.5到1.6一个重要优化是锁优化,synchronize和ReenTrantLock的性能差距就没有那么大了,主要看实现是的锁的监视对象力度粗细。自旋锁:想让线程“等待”时,不放弃CPU处理,以自己“忙循环”,等下看下锁是不是释放了,对锁释放很快的场景,可以避开线程切换开销,循环策略优化引入自适应自旋锁,尝试一定次数后放弃,进入线程等待。
锁消除:代码上要求同步,但是不可能存在共享数据竞争,可以在即时编译是消除锁。比如局部变量,stringBuffer.append().
public synchronized StringBuffer append(StringBuffer sb) { toStringCache = null; super.append(sb); return this; }
锁粗化:连续对同一个对象加锁解锁(比如循环体内),可以把锁放到外面,通过方法内联分析(inlining)。
public int arrayCount() { int result = 0; for (int i = 0; i < intArr.length; i++) { result += getEntry(i); } return result; } public synchronized int getEntry(int entry){ return intArr[entry]; } 优化后: public int arrayCount() { int result = 0; synchronized { for (int i = 0; i < intArr.length; i++) { result += intArr[entry]; } } return result; }
轻量级锁
“轻量”相对于操作系统互斥量来实现的锁而言,通过对象头部分存储锁标志(Mark Word),标识对象是不是被锁定,相当于在使用重量级锁之前,先对轻量级锁标志进行CAS操作解决一部分同步问题。
轻量级锁能提高性能的依据是经验告诉我们“绝大部分的锁,在整个生命周期內都不存在竞争”。
偏向锁 “偏向”锁偏向于第一个获得它的线程,基于轻量级锁实现,如果竞争对象标水,线程获得过锁,后面该线程在进入就同步块,CAS操作也省了。当另外一个线程来竞争时,偏向结束。
最后补一个概念
公平锁:是指竞争锁时锁分配的公平性,谁先来申请谁优先给谁,这种算“公平”,synchronized非公平,ReentrantLock默认也非公平。相关文章推荐
- 编程实践笔记{Java 线程 并发处理 Webservice}(转)
- java服务对线程并发的处理
- java8 lumbda 、Executors处理线程并发
- Java并发学习之八——在线程中处理不受控制的异常
- 编程实践笔记{Java 线程 并发处理 Webservice}
- 编程实践笔记{Java 线程 并发处理 Webservice}
- java并发编程学习:如何等待多个线程执行完成后再继续后续处理(synchronized、join、FutureTask、CyclicBarrier)
- Java多线程,线程同步synchronized,线程死锁【线程池常规用法】多线程并发处理
- java并发编程学习:如何等待多个线程执行完成后再继续后续处理(synchronized、join、FutureTask、CyclicBarrier)
- Java并发编程示例(八):处理线程的非受检异常
- java线程并发处理之Volatile关键字
- JAVA 并发编程随笔【六】线程的竞态条件与临界区
- 利用线程来处理java中进度条动态改…
- 线程高级应用-心得4-java5线程并发库介绍,及新技术案例分析
- Java并发库(十四):控制线程访问数量Semaphore
- 【Java并发编程】之三:线程挂起、恢复与终止的正确方法(含代码)
- Java 并发 中断线程
- java线程并发包util.concurrent的研究(六)
- java后台线程处理
- java 多线程 并发处理