jvm(13)-线程安全与锁优化
【0】README
0.1)本文部分文字转自“深入理解jvm”, 旨在学习 线程安全与锁优化 的基础知识;
0.2)本文知识对于理解 java并发编程非常有用,个人觉得,所以我总结的很详细;
【1】概述
【2】线程安全
1)线程安全定义:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的;(干货——线程安全定义)


2)绝对线程安全
2.1)在java API中标注自己是线程安全的类,大多数都不是绝对的线程安全
2.2)java.util.Vector 是一个线程安全的容器,因为它的add()方法,get()方法,size() 方法 这些方法都是被 synchronized修饰的,尽管效率低下,但确实是安全的;对Vector的测试如下:
// 对线程安全的容器 Vector的测试
public class VectorTest {
private static Vector<Integer> vector = new Vector<>(); public static void main(String[] args) {
while(true) {
for (int i = 0; i < 100; i++) {
vector.add(i);
} Thread removeThread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < vector.size(); i++) {
vector.remove(i);
}
}
}); Thread printThread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < vector.size(); i++) {
System.out.println(vector.get(i));
}
}
}); removeThread.start();
printThread.start(); // 不要同时产生过多的线程,否则会导致os 假死
while(Thread.activeCount() > 20);
}
}
}

对以上代码的分析(Analysis):
A1)运行结果: 作者说会抛出异常(但我的运行结果却没有抛出异常),按理说应该是会抛出异常的;
A2)抛出异常的原因:因为如果另一个线程恰好在错误的时间里删除了一个元素,导致序号i 已经不再可用的话,再用i 访问数组就会抛出一个 ArrayIndexOutOfBoundsException。
A3)如果要保证这段代码能够正确执行下去,修改后的代码为:
// 对线程安全的容器 Vector的测试(修改后的代码)
public class ModifiedVectorTest {
private static Vector<Integer> vector = new Vector<>(); public static void main(String[] args) {
while(true) {
for (int i = 0; i < 100; i++) {
vector.add(i);
} Thread removeThread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (vector) { // 添加同步块,this line
for (int i = 0; i < vector.size(); i++) {
vector.remove(i);
}
}
}
}); Thread printThread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (vector) { // 添加同步块,this line
for (int i = 0; i < vector.size(); i++) {
System.out.println(vector.get(i));
}
}
}
}); removeThread.start();
printThread.start(); // 不要同时产生过多的线程,否则会导致os 假死
while(Thread.activeCount() > 20);
}
}
}
3.1)上述 VectorTest.java 和 ModifiedVectorTest.java 就是相对线程安全的案例;
4.1)线程兼容定义:线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境中可以安全地使用;

case1)等待可中断:指当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,可中断特性对处理执行时间非常长的同步块很有帮助;1.6.3)关于synchronized 和 ReentrantLock 性能的分析:

对上图的分析(Analysis):
A1)多线程环境下 synchronized的吞吐量下降得非常严重,而 ReentrantLock 则能基本保持在同一个比较稳定的水平上;与其说ReentrantLock性能好,还不如说 synchronized还有非常大的优化余地;
A2)虚拟机在未来的性能改进中肯定也会更加偏向于原生的 synchronized,所以还是提倡在 synchronized能实现需求的情况下,优先考虑使用 synchronized 来进行同步;(干货——同步方式推荐使用synchronized)
2.4)如何使用CAS 操作来避免阻塞同步,看个荔枝:(测试incrementAndGet 方法的原子性)
// Atomic 变量自增运算测试(incrementAndGet 方法的原子性)
public class AtomicTest {
public static AtomicInteger race = new AtomicInteger(0); public static void increase() {
// 输出正确结果,一切都要归功于 incrementAndGet 方法的原子性
race.incrementAndGet();
} public static final int THREADS_COUNT = 20; public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[THREADS_COUNT];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
increase();
}
}
});
threads[i].start();
} while(Thread.activeCount() > 1) {
Thread.yield();
} System.out.println(race);
} /**
* incrementAndGet() 方法的JDK 源码
* Atomically increment by one the current value.
* @return the updated value
*/
public final int incrementAndGet() {
for(;;) {
int current = get();
int next = current + 1;
if(compareAndSet(current,next)) {
return next;
}
}
}
}
2.5)CAS操作(比较并交换操作)的ABA问题:如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就说它的值没有被其他线程改变过了吗? 如果在这段期间它的值曾经被改为了B,之后又改回了A,那CAS操作就会误认为它从来没有被改变过,这个漏洞称为 CAS操作的 ABA问题;
2.6)解决方法:J.U.C 包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的version 来保证CAS的正确性。不过目前来说这个类比较鸡肋, 大部分cases 下 ABA问题 不会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更高效;(干货——CAS操作(比较并交换操作)的ABA问题及其解决方法)

public class LockEliminateTest {
    // raw code
    public String concatString(String s1, String s2, String s3) {
        return s1 + s2 + s3;
    }
    // javac 转化后的字符串连接操作
    public String concatString(String s1, String s2, String s3) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        sb.append(s3);
        return sb.toString();
    }
}
对以上代码的分析(Analysis):
A1)对于 javac 转化后的字符串连接操作代码: 使用了同步,因为StringBuffer.append() 方法中都有一个同步块,锁就是sb对象。虚拟机观察变量sb,很快就会发现他的动态作用域被限制在 concatString() 方法内部;也就是所 sb 的所有引用都不会逃逸到方法之外;
A2)所以,虽然这里有锁,但是可以被安全地消除掉,在即时编译之后,这段代码就会忽略掉所有的同步而直接执行了;
 // javac 转化后的字符串连接操作
public String concatString(String s1, String s2, String s3) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
sb.append(s3);
return sb.toString();
}


jvm(13)-线程安全与锁优化的更多相关文章
- jvm(13)-线程安全与锁优化(转)
		
0.1)本文部分文字转自“深入理解jvm”, 旨在学习 线程安全与锁优化 的基础知识: 0.2)本文知识对于理解 java并发编程非常有用,个人觉得,所以我总结的很详细: [1]概述 [2]线程安全 ...
 - 深入理解JVM(7)——线程安全和锁优化
		
Java中的线程安全 按照线程安全的“安全程度”由强至弱来排序,可以将Java语中各种操作共享的数据分为以下5类:不可变. 绝对线程安全. 相对线程安全. 线程兼容和线程对立. 1.不可变 不变的对象 ...
 - JVM(8)  线程安全与锁优化
		
面向过程编程:程序编写以算法为核心,程序员会把数据和过程分别作为独立的部分来考虑,数据代表问题空间的客体,程序代码则用于处理这些数据.这种思维方式直接站在计算机的角度去抽象问题和解决问题,称为面向过程 ...
 - 深入理解JVM - 线程安全与锁优化 - 第十三章
		
线程安全 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方法进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对 ...
 - 《深入理解Java虚拟机》-----第13章 线程安全与锁优化
		
概述 在软件业发展的初期,程序编写都是以算法为核心的,程序员会把数据和过程分别作为独立的部分来考虑,数据代表问题空间中的客体,程序代码则用于处理这些数据,这种思维方式直接站在计算机的角度去抽象问题和解 ...
 - 深入理解java虚拟机-第13章-线程安全与锁优化
		
第十三章 线程安全与锁优化 线程安全 java语言中的线程安全 1 不可变.Immutable 的对象一定是线程安全的 2 绝对线程安全 一个类要达到不管运行时环境如何,调用者都不需要额外的同步措施, ...
 - JVM之java并发 ——线程安全与锁优化
		
概述 人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情.有时候,良好的设计原则不得不向现实 ...
 - 《深入了解java虚拟机》高效并发读书笔记——Java内存模型,线程,线程安全 与锁优化
		
<深入了解java虚拟机>高效并发读书笔记--Java内存模型,线程,线程安全 与锁优化 本文主要参考<深入了解java虚拟机>高效并发章节 关于锁升级,偏向锁,轻量级锁参考& ...
 - JVM-并发-线程安全与锁优化
		
线程安全与锁优化 1.线程安全 (1)当多个线程访问一个对象时,如果不考虑这些线程在执行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获 ...
 
随机推荐
- Oracle的日志记录模式
			
本篇摘自 http://www.cnblogs.com/cnjava/archive/2012/04/09/2439497.html --=============================== ...
 - touch: cannot touch ‘/var/jenkins_home/copy_reference_file.log’: Permission denied Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?
			
问题:在从 https://c.163.com/hub#/m/repository/?repoId=3093 下载镜像 docker pull hub.c.163.com/library/jenkin ...
 - 测试网站页面网速的Python脚本
			
一.测试网站页面网速脚本 [root@salt ~]# cat check_url.py #!/usr/bin/python # coding: UTF-8 import StringIO,pycur ...
 - 前端传递参数,由于控制器层类实现了struts2的ModelDriven而产生的一个异常
			
产生的异常如下: ognl.MethodFailedException: Method "setId" failed for object com.aliyun.pcitcAliy ...
 - docker实战——在测试中使用Docker
			
在之前几章中介绍的都是Docker的基础知识,了解什么是镜像,docker基本的启动流程,以及如何去运作一个容器等等. 接下来的几个章节将介绍如何在实际开发和测试过程中使用docker. 将Docke ...
 - 倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-电机实际运行距离跟给定距离不一致怎么办,如何设置Scaling Factor
			
有时候,让电机从0度转到绝对的360度,有时候会出现电机实际转动更多或者更少的情况. 一般是电机的编码器的Scaling Factor Numerator数值不对导致的,数值越小,则同比转过角度越 ...
 - 可伸缩Web架构与分布式系统(2)
			
开源软件近年来已变为构建一些大型网站的基础组件.并且伴随着网站的成长,围绕着它们架构的最佳实践和指导准则已经显露.这篇文章旨在涉及一些在设计大型网站时需要考虑的关键问题和一些为达到这些目标所使用的组件 ...
 - Jquery DataTables 自定义布局sdom
			
Jquery DataTables 自定义布局sdom JQuery Datatable sDom 配置 官网给的描述是: This initialisation variable allows yo ...
 - J2SE语言--百度百科
			
Java2平台包括:标准版(J2SE).企业版(J2EE)和微缩版 (J2ME)三个版本.J2SE,J2ME和J2EE,这也就是SunONE(Open NetEnvironment)体系.J2SE就是 ...
 - Struts2--百度百科
			
Struts 2是Struts的下一代产品,是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架.其全新的Struts 2的体系结构与Struts 1的体系结构差别 ...