垃圾回收算法与实现系列-线程安全与锁简介
导语
锁是多线程软件开发的必要工具,它的基本作用是保护临界区资源不被多个线程同时访问进而受到破坏。如果由于多线程访问造成数据不一致,那么系统将会得到一个错误的结果。通过锁可以让多个线程排队一个一个进入临界区访问目标对象,使目标对象的状态总是保持一致性。这个就是锁存在的价值。
锁的基本概念和实现
文章目录
- 锁的基本概念和实现
- 理解线程安全
- 对象头和锁
理解线程安全
通过锁可以实现线程安全,线程安全就是在多线程的环境下,无论多少的线程访问目标对象,目标对象的状态应该始终保持一致,线程访问得到的结果总是正确的。
通过如下的例子来看看线程安全问题。
线程A和线程B在数据库中分别读取两条库存消息,线程A读取到A商品库存是98,线程B读取到B商品库存是77。现在需要将从数据库中获取到数据保存到对象实例S中,再进行其他的相应的业务逻辑处理。这个时候,对象实例S就是临界区资源。如果没有锁机制对其进行保护,任意通过A线程和B线程随意处理,由于线程之间的访问是无序的,一种可能的访问结果是线程A将库存改为97,接着线程B将库存改成76,这样覆盖了A线程的操作,然后线程B将库存76的的对象,这样子就导致整个的实体对象S的信息被修改了。这样就会出现一个数据不一致的问题。
这个问题就可以通过锁机制来解决,在线程A操作实体对象S的时候对对象进行加锁操作。每次只允许一个线程对其进行操作,等到操作完成之后,释放锁。然后再由其他线程进行锁竞争。这样就可以很好的解决这个问题。
public class ThreadUnsafe{public static List<Integer> numberList = new ArrayList<>();public static class AddToList implements Runnable{int startnum = 0;public AddToList(int startnumber){startnum = startnumber;}@Overridepublic void run(){int count = 0;while(count<1000000){numberList.add(startnum);startnum+=2;count++;}}}public static void main(String[] args) throws InterruptedException{Thread t1 = new Thread(new AddToList(0));Thread t2 = new Thread(new AddToList(1));t1.start();t2.start();}
}
上面代码中,两个线程同时向着numberList增加数据,由于ArrayList不是线程安全的,所以程序运行之后可能会导致数组越界的异常。
出现这个问题是因为两个线程同时对ArrayList进行写操作,破坏了ArrayList内部数据的一致性,导致其中的一个线程访问到了错误的数组索引,一个最简单的方式就是利用一个线程安全的对象来代替ArrayList,例如Vector 它就是内部实现了锁机制,属于线程安全的。
public synchronized boolean add(E e){modCount++;ensureCapacityHelper(elementCount+1);elementData[elementCount++] = e;return true;
}
关键字synchronized保证了每次只有一个线程可以访问对象实例,确保了多线程环境中对象内部数据的一致性。
对象头和锁
在JVM中每个对象都有一个对象头,用于保存对象的系统信息。对象头中有一个称为Mark Word 的部分,它是实现锁机制的关键。在32位系统中,Mark Work为一个32位的数据,在64位系统中,是64位。它是一个多功能的数据区,可以存放对象的哈希值、对象年龄、锁的指针等信息,一个对象是否占用锁、占用哪个锁,就记录在这个Mark Word中。
以32位的系统为例,普通对象的对象头如下
hash:25--------------------------->|age:4 biased_lock:1 lock:2
它表示Mark Word中有25位表示对象的hash 值,4位表示年龄,1位表示偏向锁,2位表示锁信息。
对于偏向锁的对象,格式如下
[JavaThread* |epoch|age|1|01]
前23位表示持有偏向锁的线程,后续2位表示偏向锁的时间戳(epoch),4位表示对象年龄,年龄后1位固定位1,表示偏向锁,最后2位为01,表示可偏向/未锁定。
当对象处于轻量级锁定的时候,其Mark Word如下
[ptr |00] looked
此时,它指向存放在获得锁的线程栈中的该对象的真实对象头。
当对象处于重量级锁定的时候,其Mark Word 如下
[ptr |10] monitor
此时,最后2位为10,整个Mark Word表示指向Monitor的指针。
当对象处于普通的未锁定状态的时候,其格式如下
[header |0|01] unlock
前29位表示对象的哈希值、年龄等信息。倒数第3位为0,最后两位为01,表示未锁定。可以发现,最后两位的值和偏向状态是一样的,此时虚拟机通过倒数第3位来区分是否为偏向锁。
垃圾回收算法与实现系列-线程安全与锁简介相关推荐
- 垃圾回收算法与实现系列-学习GC之前的准备工作
导语 在学习垃圾回收算法之前,首先需要了解什么是Heap.什么是Root.什么是Object.什么是Stack.什么是Pointer,这写概念都是什么,为什么要在垃圾回收算法中使用,使用这些东西有 ...
- 垃圾回收算法与实现系列-String在虚拟机中的实现
导语 String 字符串一直作为各种编程语言的核心内容存在.作为动态字符的一种是实现方案,应用很广泛.每一种计算机语言对于这种数据结构都进行了特殊的优化和实现.在Java中,String作为引用 ...
- 垃圾回收算法与实现系列-Java堆内存溢出原因
导语 内存一直是所有开发人员探索的一片天地,再JVM中,内存往往会被分为几块,了解不同的内存区域对编写出优质的代码有很大的帮助.堆内存作为JVM中比较重要的区域,有很多值得我们探索的地方.下面就来 ...
- 垃圾回收算法与实现系列-GC 标记-清除算法
导语 在GC 中最重要的算法就是GC标记-清除算法(Mark-Sweep GC).在很多的场景下都还是在使用这个算法来进行垃圾回收操作.就如如同它的名字一样先标记,然后清除.下面就来看看标记清除算 ...
- 垃圾回收算法与实现系列-JVM无锁实现
导语 为了确保多线程场景下数据安全,使用锁机制一直是一种优秀的解决方案,但是再高并发场景下,对锁的竞争可能成为性能瓶颈.为此,有出现了一种新的解决方案,被称为是非阻塞同步的方案.这种实现方式不需要 ...
- 垃圾回收算法与实现系列-锁在应用层的优化思路
导语 之前的分享中主要介绍了虚拟机内部的对锁机制的优化与具体实现,在实际的开发过程中,还可以通过在应用层的合理优化,达到保证性能的目的,那么下面就学习介绍一下在应用层中如何进行锁的优化. 文章目录 ...
- 垃圾回收算法与实现系列-锁在Java虚拟机中的实现和优化
导语 上篇分享中提到了对象头Mark Word 的基本概念之后,接下来就可以深入到虚拟机内部了.在多线程程序中,线程之间的竞争是不可避免的,并且这是一种多线程程序的常态.那么如何高效的处理多线程的 ...
- 垃圾回收算法与实现系列-Java的Class文件详解
导语 对于JVM来说,Class文件作为虚拟机的一个重要接口.无论使用什么样的语言,进行软件开发,只要能将源码编译为Class文件,并放到正确的路径下,那么这种语言就可以被JVM所执行.可以说Cl ...
- bat判断文件是否存在_BAT面试必问题系列:JVM判断对象是否已死和四种垃圾回收算法总结...
JVM系列: 面试题一:判断对象是否已死 判断对象是否已死就是找出哪些对象是已经死掉的,以后不会再用到的,就像地上有废纸.饮料瓶和百元大钞,扫地前要先判断出地上废纸和饮料瓶是垃圾,百元大钞不是垃圾.判 ...
最新文章
- 基于MySQL数据库下亿级数据的分库分表
- 数据中心UPS电源节能降耗的四大原则
- 离散数学关系的基本运算和关系的性质闭包
- C# Color颜色对照表
- python rsa加密二进制文件_用Python中的RSA加密文件
- java调python画图_Python Matplotlib plot 绘图
- Java线程中断机制-如何中断线程
- ubuntu14.04 设置静态ip
- IT群侠传第一回内外兼修
- log4j 配置文件参数说明
- c语言编程图形并颜色,C语言图形编程(三、绘图函数1)
- 为什么要远离色情信息(转载)
- 计算机软件如何永久删除,如何彻底删除电脑软件
- Laraval-admin 自定义form组件
- mysql auto.cnf_MySQL之my.cnf配置
- textarea文本框的placeholder文字换行
- 如何阻止事件冒泡与默认事件?
- 【轻量型卷积网络】ResNeXt网络解析
- 【愚公系列】2023年04月 攻防世界-MOBILE(Android2.0)
- 英语单词的形象联想组合记忆法