Java 多线程与并发【原理第一部分笔记】

Synchronized

synchronized的基本含义以及使用方式

在Java中线程安全问题的主要诱因就是存在共享数据(也称为临界资源)以及存在多条线程共同操作这些共享数据

解决问题的根本方法是,在同一时刻有且只有一个线程在操作共享数据,其他数据必须等到该线程处理完数据后再对共享数据进行操作

此时就引入了互斥锁,互斥锁的特性有互斥性(即同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程的协调机制,这样在同一时间只有一个线程对需要同步的代码块(复合操作)进行访问,互斥性也称为原子性)以及可见性(必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作,从而引起不一致)

需要明确的一点是synchronized锁的不是代码,锁的都是对象

根据获取的锁的分类,可以分为获取对象锁以及获取类锁

对于获取对象锁来说,主要有两个用法,第一个是同步代码块(synchronized(this),synchronized(类实例对象)),锁是小括号中的实例对象,第二个就是同步非静态方法(synchronized method),锁是当前对象的实例对象

对于获取类锁来说,主要有两个用法,第一个是同步代码块(synchronized(类.class)),锁是小括号中的类对象(class对象),第二个就是同步静态方法(synchronized static method),锁是当前对象的类对象(class对象)

对象锁和类锁的总结

1.有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块

2.若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步代码块的线程就会被阻塞

3.若锁住的是用一个对象,那么一个线程在访问对象的同步方法时,另一个访问对象同步方法的线程就会被阻塞

4.若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象同步方法的线程会被阻塞,反之亦然

5.同一个类的不同对象的对象锁互不干扰

6.类锁由于也是一种特殊的对象锁,因此表现和上述1,2,3,4一致,而由于一个类只有一把对象锁,所以同一个类的不同对象使用类锁将会是同步的

7.类锁和对象锁互不干扰

synchronized底层实现的原理

实现synchronized的基础是Java对象头和monitor

对象在内存中的布局有三块区域,分别是对象头,实例数据以及对齐填充,主要说一下对象头

对象头的结构,主要由Mark word和class metadata address组成

Mark Word是一个非固定的数据结构,以便于存储更多的有效数据,会根据对象的状态来调整自己

在Java的设计中,每个对象自出生就带了一把看不见的锁,其称为内部锁,或者是monitor锁,可以将monitor理解为是一个同步工具,通常其被描述为一个对象

monitor锁的竞争,获取以及释放

自旋锁

许多情况下,共享数据的锁定状态持续时间比较短,切换线程不太值得,自旋锁是怎么实现的呢,即通过让线程执行忙循环等待锁的释放,不让出CPU,Java6以后默认开启,缺点就是,如果锁被其他的线程长时间的占用,会带来许多性能上的开销,因此,应该进行限定,但是比较困难去判定这个限定的界限,因此就需要一个更好的来代替

自适应自旋锁

自适应自旋锁自旋的次数不再固定,而是由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定

锁消除

锁消除是虚拟机另一种锁优化,这种锁优化更加的彻底,在JIT编译时,通过对运行上下文进行扫描,去除不可能存在竞争的锁,通过这种方式来消除没有必要的锁,可以节省毫无意义的时间

锁粗化

这是另一个极端,这是在存在有一连串操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中,那即便是没有线程竞争,频繁的锁操作也会导致不必要的性能操作,这是就可以通过扩大加锁的范围,避免反复的加锁和解锁

synchronized的四种状态:

这四种状态分别是,无锁,偏向锁,轻量级锁,重量级锁,会随着竞争情况逐渐升级,锁降级也是会发生的,在jvm进入前一个安全点的时候会检查是否有闲置的monitor,然后看一下是不是要降级,但是主要还是发生锁升级(锁膨胀)

锁膨胀的方向是,无锁->偏向锁->轻量级锁->重量级锁,主要说一下偏向锁和轻量级锁

偏向锁

在大多数情况下,锁不存在多线程竞争,总是由同一个线程多次获得,因此为了减少同一个线程获取锁的代价,就引入了偏向锁,偏向锁的核心思想是,如果一个线程获得了锁,那么锁就进入了偏向模式,此时Mark Word的结构也变为偏向锁结构,当线程再次请求锁的时候,无需再做任何同步操作,即获取锁的过程只需要检查Mark Word的锁标记为偏向锁以及当前线程id等于Mark Word的threadid即可,这样就省去了大量有关锁申请的操作,偏向锁在失败以后并不会立刻膨胀为重量级锁

这种不适合用于锁竞争比较激烈的多线程场合

轻量级锁

轻量级锁是由偏向锁升级得到的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁,轻量级锁适合用于存在线程交替执行同步块的场景,如果存在同一时间访问同一个锁的情况,那么就会导致轻量级锁膨胀为重量级锁

锁的内存语义

当线程释放锁的时候,Java内存模型会把该线程对应的本地内存中的共享变量刷新到主内存中,当线程获取锁的时候,Java内存模型会把该线程对应的本地内存置为无效,从而使得被监视器保护的临界区代码必须从主内存中读取共享变量

轻量级锁的加锁过程

1.在代码进入同步块的时候,如果同步对象锁状态为无锁状态,那么虚拟机首先将当前线程的栈帧中建立一个名为锁记录的空间,用于存储对象目前的Mark Word的拷贝

2.将拷贝对象头中的Mark Word复制到锁记录中

3.拷贝成功以后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向lock record的指针,并将lock record里的owner指针指向object Mark Word,如果成功了就执行第四步,否则执行第五步

4.如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为00,即表示此对象处于轻量级锁定状态

5.如果失败了,虚拟机首先检查对象的Mark Word是不是指向当前的线程的栈帧,如果是就说明当前的栈帧已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则就说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁的标志的状态值就为10,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态,而当前线程便尝试使用自旋来获取锁,

轻量级锁的解锁过程

1.通过CAS操作尝试把线程中复制的displaced Mark Word对象替换为当前的Mark Word

2.如果替换成功了,整个同步过程就完成了

3.如果替换失败了,说明还有其他的线程尝试获取过该锁(此时锁已经膨胀了),那么就要在释放锁的同时,唤醒挂起来的线程

偏向锁,轻量级锁以及重量级锁的汇总

synchronized和reentrantlock的区别

reentrantlock(再入锁)

其位于java.util.concurrent.locks包,和countdownlatch,futuretask以及semaphore一样基于AQS实现的,而且reentrantlock可以实现比起synchronized更细粒度的控制,比如控制fairness,但是在编码中要注意必须要明确unlock()方法的释放,在调用lock()之后,必须调用unlock()释放锁,需要注意的是,reentrantlock的性能并不一定就比synchronized高,需要看场景,而且其也是可重入的

reentrantlock公平性的设置

使用reentrantlock fairlock = new reentrantlock(true)就可以设置,在参数为true的时候,更倾向于将锁赋予等待时间最久的线程,这就是所谓的公平锁,即获取锁的顺序按先后调用lock方法的顺序来进行,不过慎用,只有在程序必须要使用这种情况的时候才使用是最好的,而所谓的非公平锁就是抢占的顺序不确定,一切看运气,运气好就抢得到,其中synchronized就是非公平锁

reentrantlock将锁对象化

可以判断是否有线程,或者某个特定线程,在排队等待获取锁,并且可以进行带超时的获取锁的尝试,而且还可以感知到有没有成功获取到锁

reentrantlock能不能将wait,notify以及notifyall对象化呢?

可以的,可以使用Java.util.concurrent.locks.condition

对synchronized和reentrantlock的区别的总结

1.synchronized是关键字,而reentrantlock是类

2.reentrantlock可以对获取锁的等待时间进行设置,避免死锁,而且reentrantlock可以获取各种锁的信息,此外,reentrantlock还可以灵活的实现多路通知

3.二者的锁机制不一样,synchronized是操作对象头中的Mark Word,而reentrantlock是调用UNsafe类中的park()方法

Java 多线程与并发【原理第一部分笔记】的更多相关文章

  1. Java 多线程与并发【原理第二部分笔记】

    Java 多线程与并发[原理第二部分笔记] 什么是Java内存模型中的happens-before Java内存模型,即JMM,本身是一种抽象的概念,并不是真实存在的,他描述的是一组规则或者说是一种规 ...

  2. Java 多线程高并发编程 笔记(一)

    本篇文章主要是总结Java多线程/高并发编程的知识点,由浅入深,仅作自己的学习笔记,部分侵删. 一 . 基础知识点 1. 进程于线程的概念 2.线程创建的两种方式 注:public void run( ...

  3. Java 多线程与并发【知识点笔记】

    Java 多线程与并发[知识点笔记] Java多线程与并发 先说一下线程与进程的由来: 在初期的计算机,计算机只能串行执行任务,并且需要长时间的等待用户的输入才行 到了后来,出现了批处理,可以预先将用 ...

  4. JAVA 多线程和并发学习笔记(三)

    Java并发编程中使用Executors类创建和管理线程的用法 1.类 Executors Executors类可以看做一个“工具类”.援引JDK1.6 API中的介绍: 此包中所定义的 Execut ...

  5. Java 多线程:并发编程的三大特性

    Java 多线程:并发编程的三大特性 作者:Grey 原文地址: 博客园:Java 多线程:并发编程的三大特性 CSDN:Java 多线程:并发编程的三大特性 可见性 所谓线程数据的可见性,指的就是内 ...

  6. Java多线程与并发模型之锁

    这是一篇总结Java多线程开发的长文.文章是从Java创建之初就存在的synchronized关键字引入,对Java多线程和并发模型进行了探讨.希望通过此篇内容的解读能帮助Java开发者更好的理清Ja ...

  7. 031.[转] 从类状态看Java多线程安全并发

    从类状态看Java多线程安全并发 pphh发布于2018年9月16日 对于Java开发人员来说,i++的并发不安全是人所共知,但是它真的有那么不安全么? 在开发Java代码时,如何能够避免多线程并发出 ...

  8. JAVA多线程和并发基础面试问答(转载)

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  9. [转] JAVA多线程和并发基础面试问答

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

随机推荐

  1. gitlab 设置tag保护及取消tag保护功能

    用gitlab管理员登录系统 进入项目->设置->Repository 设置项目的Tag保护 效果展示 取消Tag保护 效果展示

  2. MindSpore模型精度调优实战:常用的定位精度调试调优思路

    摘要:在模型的开发过程中,精度达不到预期常常让人头疼.为了帮助用户解决模型调试调优的问题,我们为MindSpore量身定做了可视化调试调优组件:MindInsight. 本文分享自华为云社区<技 ...

  3. python 06篇 常用模块

    一.模块 什么是模块? 模块实质上就是一个python文件,它是用来组织代码的,意思就是说把python代码写到里面,文件名就是模块的名称,test.py test就是模块名称. 1.1 导入模块 i ...

  4. 两人团队项目-石家庄地铁查询系统(web版)

    大二上学期做过只有两号线的地铁查询系统,但是只能在控制台操作.这一次将线路加到了六条,并且要求web实现,下面简述一下设计思路和具体代码实现: 1.数据库建表 于我自己习惯而言,我写javaweb项目 ...

  5. 12. Mysql基础入门

    课程大纲 • 数据库概述 • MySQL基本操作 • MySQL索引基础 • MySQL高级特性

  6. 如何快速更新长缓存的 HTTP 资源

    前言 HTTP 缓存时间一直让开发者头疼.时间太短,性能不够好:时间太长,更新不及时.当遇到严重问题需紧急修复时,尽管后端文件可快速替换,但前端文件仍从本地缓存加载,导致更新长时间无法生效. 对于这个 ...

  7. Greenplum安装总结

    Greenplum安装总结 一.环境说明 服务器centos7 4台,一台Master节点,三台Segment节点: mdw 192.168.43.21 (master节点) sdw1 192.168 ...

  8. springMVC-8-jackson使用

    springMVC默认的 Json 解决方案是 Jackson, 所以只需要导入 Jackson 的 jar, 即可使用 <!--Jackson--> <dependency> ...

  9. 解析ArrayList的底层实现(上)

    private static final long serialVersionUID = 8683452581122892189L;//唯一序列号ID private static final int ...

  10. Oracle常用SQL语句大全

    常用Oracle数据库SQL语句汇总. 1.常用操作 --清空回收站purge recyclebin;--查询回收站select * from recyclebin--查询Oracle版本信息sele ...