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. [心得]redis集群环境搭建的错误

    安装redis集群需要版本号在3.0以上 redis-cluster安装前需要安装ruby环境 搭建集群需要使用到官方提供的ruby脚本. 需要安装ruby的环境. yum -y install ru ...

  2. 常见链表操作-链表中环的检测(JAVA实现)

    问题如何检测一个单链表中是否有环,例如下图的例子. 解决思路1:快慢指针法这是最常见的方法.思路就是有两个指针P1和P2,同时从头结点开始往下遍历链表中的所有节点. P1是慢指针,一次遍历一个节点.P ...

  3. ESP32-http client笔记

    基于ESP-IDF4.1 #include <string.h> #include <stdlib.h> #include "freertos/FreeRTOS.h& ...

  4. SQLITE数据库不支持远程访问

    SQLITE数据库不支持远程访问 import sqlite3 conn=sqlite3.connect("dailiaq.db") cur=conn.cursor() def c ...

  5. qtscrcpy使用

    点击"USB线"一栏中的"刷新设备列表"按钮,随后设备序列号会显示出来: ·点击"获取设备IP",随后在"无线"一栏中会 ...

  6. 三、k8s集群可用性验证与调参(第一章、k8s高可用集群安装)

    作者:北京小远 出处:http://www.cnblogs.com/bj-xy/ 参考课程:Kubernetes全栈架构师(电脑端购买优惠) 文档禁止转载,转载需标明出处,否则保留追究法律责任的权利! ...

  7. [006] - JavaSE面试题(六):泛型

    第一期:Java面试 - 100题,梳理各大网站优秀面试题.大家可以跟着我一起来刷刷Java理论知识 [006] - JavaSE面试题(六):泛型 第1问:什么是泛型? Java泛型( generi ...

  8. 【论文阅读】Motion Planning through policy search

    想着CSDN还是不适合做论文类的笔记,那里就当做技术/系统笔记区,博客园就专心搞看论文的笔记和一些想法好了,[]以后中框号中间的都算作是自己的内心OS 有时候可能是问题,有时候可能是自问自答,毕竟是笔 ...

  9. YsoSerial 工具常用Payload分析之CC1

    前文介绍了最简单的反序列化链URLDNS,虽然URLDNS本身不依赖第三方包且调用简单,但不能做到漏洞利用,仅能做漏洞探测,如何才能实现RCE呢,于是就有Common-collections1-7.C ...

  10. BigDecimal之除不尽报错

    当bigdecimal除不尽(循环小数)后会报错,下面的是BigDecimal ,divide方法提供的精确小数方法(推荐使用) BigDecimal avgCapital = loanAmount. ...