读书笔记之《Java并发编程的艺术》—— 二

由于内容过多,分一个系列来写,这是第二篇。

三、Java内存模型

1、Java内存模型的基础

线程之间的通信机制有两种:共享内存和消息传递。

在Java里,所有实例域、静态域和数组元素都存储在堆内存中,堆内存在线程之间共享。局部变量、方法定义参数、异常处理参数不会在线程间共享。Java线程之间的通信由Java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。线程之间的共享变量存储在主内存,每个线程都有一个私有的本地内存(本地内存是JMM的一个抽象概念,并不真实存在)。JMM通过控制主内存与每个线程的本地内存之间的交互,来提供内存可见性保证。

执行程序时,为了提高性能,编译器和处理器常常会对指令进行重排序(编译器优化的重排序,指令级并行的重排序,内存系统的重排序)。

为了保证内存可见性,Java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特定类型的处理器重排序。JMM把内存屏障指令分为四类:LoadLoad、StoreStore、LoadStore、StoreLoad。 StroeLoad是一个全能型的屏障,同时具有其他3个屏障的效果。

2、volatile的内存语义

volatile写的内存语义:
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程从主内存中读取共享变量。

在每个volatile写操作的前面插入一个StoreStore屏障,在每个volatile写操作的后面插入一个StoreLoad屏障,在每个volatile读操作的后面插入一个LoadLoad屏障,在每个volatile读操作的后面插入一个LoadStore屏障。

volatile仅仅保证对单个volatile变量的读/写具有原子性,而锁的互斥特性可以确保对整个临界区代码的执行具有原子性。

3、锁的内存语义

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

四、Java并发编程基础

这一部分很多内容和《Java多线程编程核心技术》重复,这里就不再记录了。

五、Java中的锁

1、Lock接口

跟synchronized的隐式获取锁相比,Lock接口在使用时显示的获取锁和释放锁。

public interface Lock {
    // 获取锁,调用该方法当前线程将会获取锁,当锁获得后,从该方法返回
    void lock();
    // 可中断地获取锁,和lock()方法不同的是,在该方法中会响应中断,即在锁的获取中可以中断当前线程
    void lockInterruptibly() throws InterruptedException;
    // 尝试非阻塞的获取锁,调用该方法后立即返回,如果能获取则返回true,否则返回false
    boolean tryLock();
    // 在指定的截止时间之前获取锁,如果截止时间到了仍旧没有获取锁,则返回
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    // 释放锁
    void unlock();
    // 获取等待通知组件,该组件和当前的锁绑定,当前线程只有获得了锁,才能调用该组件的wait()方法
    Condition newCondition();
}

2、队列同步器

队列同步器AbstractQueuedSynchronizer(AQS)是用来构建锁或其他同步组件的基础框架,它使用一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。

同步器的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态。子类推荐被定义为自定义同步组件的静态内部类,同步器自身仅仅是定义了若干同步状态获取和释放的方法来供自定义同步组件使用。同步器即支持独占式地获取同步状态,也支持共享式地获取同步状态。

(1)同步器的设计是基于模板方法模式的,也就是说,使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器实现的模板方法,而这些模板方法将会调用使用者重写的方法。

同步器提供了3个方法来访问或修改同步状态:

// 同步状态,加了volatile关键字
private volatile int state;

// 获取当前同步状态
protected final int getState() {
    return state;
}

// 设置当前同步状态
protected final void setState(int newState) {
    state = newState;
}

// 使用CAS设置当前状态,该方法能够保证状态设置的原子性
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

同步器可以重写的方法:

// 独占式获取同步状态
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}
// 独占式释放同步状态
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}
// 共享式获取同步状态
protected int tryAcquireShared(int arg) {
    throw new UnsupportedOperationException();
}
// 共享式释放同步状态
protected boolean tryReleaseShared(int arg) {
    throw new UnsupportedOperationException();
}
// 当前同步器是否在独占模式下被线程占用
protected boolean isHeldExclusively() {
    throw new UnsupportedOperationException();
}

实现自定义同步组件时,将会调用同步器提供的模板方法,比如下面几个模板方法:

// 独占式获取同步状态
public final void acquire(int arg)
// 共享式获取同步状态
public final void acquireShared(int arg)
// 独占式释放同步状态
public final boolean release(int arg)
// 共享式释放同步状态
public final boolean releaseShared(int arg)
// 获取等待在同步队列上的线程集合
public final Collection<Thread> getQueuedThreads()

同步器提供的模板方法基本上分为三类:独占式获取和释放同步状态、共享式获取和释放同步状态、查询同步队列中的等待线程情况。

(2)队列同步器的实现分析

同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个点(Node)并将其加入到同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。

同步队列中的节点用来保存获取同步状态失败的线程引用、等待状态以及前驱和后继节点:

static final class Node {
    ...
    volatile int waitStatus;
    volatile Node prev;
    volatile Node next;
    volatile Thread thread;
    Node nextWaiter;
    ...
}

同步器拥有首节点和尾结点,没有成功获取同步状态的线程会成为节点加入到队列的尾部。头结点拥有同步状态。

独占式同步状态获取和释放过程总结:在获取同步状态时,同步器维护一个同步队列,获取状态失败的线程都会被加入到队列中并在队列中进行自旋;移出队列(停止自旋)的条件是前驱节点为头结点且成功获取了同步状态。在释放同步状态时,同步器调用release(int arg)释放同步状态,然后唤醒头结点的后继节点。

共享式同步状态获取和独占式同步状态获取的最主要区别在于同一时刻能否有多个线程同时获取到同步状态。共享式获取的自旋过程中,成功获取到同步状态并退出自旋的条件就是tryAcquireShared(int arg)方法返回值大于等于0。释放调用releaseShared(int arg),释放同步状态后,将会唤醒后续处于等待状态的节点。释放同步状态的操作会同时来自多个线程。

独占式超时获取同步状态,可以在指定的时间段内获取同步状态。

3、重入锁

重入锁ReentrantLock,支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁,该锁还支持获取锁时的公平和非公平性选择。如果在绝对时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。公平的锁机制往往没有非公平的锁机制的效率高。

重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,那么再次成功获取;锁最终释放是指,线程重复n次获得了锁,随后在第n次释放该锁后,其他线程能够获得该锁。锁获取时进行计数自增,锁释放时进行计数自减,当计数等于0时表示锁已经成功释放。

4、读写锁

排它锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程均被阻塞(注意:其他的写线程,当前的写线程可以重入,即写线程获取了写锁之后能够再次获取到写锁)。读写锁的性能比排它锁好,大部分场景是读多写少的,在读多写少的情况下,读写锁能够提供比排它锁更好的并发性和吞吐量。

读写锁维护了一对锁,一个读锁一个写锁。读写锁的自定义同步器需要在同步状态(一个整形变量)上维护多个读线程和一个写线程的状态。高16位表示读,低16位表示写。

如果当前线程在获取写锁时,读锁已经被获取或者该线程不是已经获取写锁的线程,那么当前线程进入等待状态。只有等待其他读线程都释放了读锁,写锁才能被当前线程获取,而写锁一旦被获取,则其他读写线程的后续访问均被阻塞。

在没有其他写线程访问时(写状态为0),读锁总会被成功地获取。如果当前线程在获取读锁时,写锁已被其他线程获取,则进入等待状态。如果当前线程获取了写锁或者写锁未被获取,那么当前线程获取读锁。

锁降级:写锁降级为读锁,指的是把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。

5、LockSupport工具

LockSupport定义了一组以park开头的方法用来阻塞当前线程,以及unpark方法唤醒一个被阻塞的线程。

6、Condition接口

Condition接口提供了类似Object的监视器方法,Condition对象是由Lock对象创建出来的,获取一个Condition必须通过Lock的newCondition()方法。ConditionObject是AQS的内部类,每个Condition对象都包含着一个等待队列,这是实现等待/通知功能的关键。同步器拥有一个同步队列和多个等待队列。同步队列和等待队列中节点类型都是AQS里的Node。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
面试题包含了不同技术层面的面试问题,同时也能对一些没有面试开发经验的小白给予不可估量的包装, 让你的薪水绝对翻倍, 本人亲试有效.Java面试题84集、java面试专属及面试必问课程,所有的面试题有视屏讲解, 解答方案.以下是部分目录: java面试题01.面试的整体流程.mp4 │ Java面试题02.java的垮平台原理.mp4 │ Java面试题03.搭建一个java的开发环境.mp4 │ Java面试题04.java中int占几个字节.mp4 │ Java面试题05.java面向对象的特征.mp4 │ Java面试题06.装箱和拆箱.mp4 │ Java面试题07.==和equals的区别.mp4 │ Java面试题08.String.mp4 │ Java面试题09.讲一下java中的集合.mp4 │ Java面试题10.ArrayList LinkedList.mp4 │ Java面试题11.HashMap和HashTable的区别.mp4 │ Java面试题12.实现一个拷贝文件的类使用字节流还是字符串.mp4 │ Java面试题13.线程的实现方式 怎么启动线程怎么区分线程.mp4 │ Java面试题14.线程并发库和线程池的作用?.mp4 │ Java面试题15.设计模式和常用的设计模式.mp4 │ Java面试题16.http get post请求的区别.mp4 │ Java面试题17.说说你对Servlet的理解.mp4 │ Java面试题18.Servlet的生命周期.mp4 │ Java面试题19.forward和redirect的区别.mp4 │ Java面试题20.jsp和Servlet的相同点和不同点?.mp4 │ Java面试题21.内置对象和四大作用域和页面传值.mp4 │ Java面试题22.Session和Cookie的区别.mp4 │ Java面试题23.mvc模式和mvc各部分的实现.mp4 │ Java面试题24.数据库分类和常用数据库.mp4 │ Java面试题25.关系型数据库的三范式.mp4 │ Java面试题26.事务的四大特征.mp4 │ Java面试题27.mysql数据库最大连接数.mp4 │ Java面试题28.mysql和oracle的分页语句(着重说思路).mp4 │ Java面试题29.触发器的使用场景.mp4 │ Java面试题30.存储过程的优点.mp4 │ Java面试题31.jdbc调用存储过程.mp4 │ Java面试题32.简单说一下你对jdbc的理解.mp4 │ Java面试题33.写一个jdbc的访问oracle的列子.mp4 │ Java面试题34.jdbc中preparedStatement比Statement的好处.mp4 │ Java面试题35.数据库连接池的作用.mp4 │ Java面试题36.HTML.mp4 │ Java面试题37.简单介绍了一下Ajax.mp4 │ Java面试题38.js和JQuery的关系.mp4 │ Java面试题39.jQuery中的常用选择器.mp4 │ Java面试题40.jQuery中页面加载完毕事件.mp4 │ Java面试题41.jQuery中Ajax和原生js实现Ajax的关系.mp4 │ Java面试题42.简单说一下html5.mp4 │ Java面试题43.简单说一下css3.mp4 │ Java面试题44.bootstrap的是什么.mp4 │ Java面试题45.什么是框架.mp4 │ Java面试题46.简单介绍一下MVC模式.mp4 │ Java面试题47.简单说一下对mvc框架的理解.mp4 │ Java面试题48.struts2的执行流程或者struts2的原理.mp4 │ Java面试题49.Struts2的拦截器是什么?你都用它干什么?.mp4 │ Java面试题50.Spring MVC的执行流程.mp4 │ Java面试题51.SpringMVC和Struts2的不同.mp4 │ Java面试题52.简单介绍一下Spring或者Spring的两大核心.mp4 │ Java面试题53.AOP是什么?都用它做什么?.mp4 │ Java面试题54.Spring事务的传播特性和隔离级别.mp4 │ Java面试题55.ORM是什么?ORM框架是什么?.mp4 │ Java面试题56.ibatis和hibernate有什么不同.mp4 │ Java面试题57.hibernate对象状态及其转换.mp4 │ Java面试题58:hibernate的缓存.mp4 │ Java面试题59.webservice的使用场景.mp4 │ Java面试题60.Activiti的简单介绍.mp4 │ Java面试题61.linux的使用场景.mp4 │ Java面试题62.linux常用命令.mp4 │ Java面试题63:怎么操作linux服务器.mp4 │ Java面试题64:有没有使用过云主机.mp4 │ Java面试题65:数据库优化方面的事情.mp4 │ Java面试题66:如果查询和定位慢查询.mp4 │ Java面试题67:数据库优化之数据库表设计遵循范式.mp4 │ Java面试题68:选择合适的数据库引擎.mp4 │ Java面试题69:选择合适的索引.mp4 │ Java面试题70:使用索引的一些技巧.mp4 │ Java面试题71:数据库优化之分表.mp4 │ Java面试题72:数据库的读写分离.mp4 │ Java面试题73:数据库优化之缓存.mp4 │ Java面试题74:sql语句优化小技巧.mp4 │ Java面试题75:批量插入几百万条数据.mp4 │ Java面试题76:有没有使用过redis.mp4 │ Java面试题77:redis的使用场景.mp4 │ Java面试题78:redis存储对象的方式.mp4 │ Java面试题79:redis数据淘汰机制.mp4 │ Java面试题80:java访问redis级redis集群?.mp4 │ Java面试题81:微信公众号分类和微信开发原理.mp4 │ Java面试题82:怎么把微信和业务平台进行绑定.mp4 │ Java面试题83:项目的分类和项目参与者.mp4 │ Java面试题84:项目流程和业务注意事项.mp4 │ 面试必问-Mysql索引背后的故事 │ ├─java面试专属 │ ├─1.面试必考之HashMap源码分析与实现 │ │ 1.面试必考之HashMap源码分析与实现.mp4 │ │ │ ├─2.探索JVM底层奥秘ClassLoader源码分析与案例讲解 │ │ 2.探索JVM底层奥秘ClassLoader源码分析与案例讲解.wmv │ │ │ ├─3.锁、分布式锁、无锁实战全局性ID-悟空 │ │ 3.锁、分布式锁、无锁实战全局性ID-悟空.mp4 │ │ │ ├─4.SpringMvc深入理解源码分析 │ │ 4.SpringMvc深入理解源码分析-悟空.mp4 │ │ │ ├─5.Nosql Redis Jedis常用命令 │ │ 5.Nosql Redis Jedis常用命令-悟空.mp4 │ │ │ ├─6.互联网系统垂直架构之Session解决方案 │ │ 6.互联网系统垂直架构之Session解决方案.mp4 │ │ │ ├─7.分布式框架ZooKeeper之服务注册与订阅 │ │ 7.分布式框架Zookeeper之服务注册与订阅.mp4 │ │ │ ├─8.高性能网络编程必备技能之IO与NIO阻塞分析 │ │ 8.高性能网络编程必备技能之IO与NIO阻塞分析.mp4 │ │ │ ├─9.JAVA并发编程之多线程并发同步业务场景与解决方案 │ │ 9.JAVA并发编程之多线程并发同步业务场景与解决方案.wmv │ │ │ ├─10.微服务架构之Spring Cloud Eureka 场景分析与实战 │ │ 10.微服务架构之Spring Cloud Eureka 场景分析与实战.wmv │ │ │ ├─11.高性能必学之Mysql主从架构实践 │ │ 11.高性能必学之Mysql主从架构实践.mp4 │ │ │ ├─12.架构师不得不知道的Spring事物不能回滚的深层次原因 │ │ 12.架构师不得不知道的Spring事物不能回滚的深层次原因.mp4 │ │ │ ├─13.RPC底层通讯原理之Netty线程模型源码分析 │ │ 13.RPC底层通讯原理之Netty线程模型源码分析.wmv │ │ │ ├─14.分库分表之后分布式下如何保证ID全局唯一性 │ │ 14.分库分表之后分布式下如何保证ID全局唯一性.mp4 │ │ │ └─15.大型公司面试必答之数据结构与算法精讲 │ 大型公司面试必答之数据结构与算法(一)-达摩老师.mp4 │ 大型公司面试必答之数据结构与算法(二).mp4 │ ├─面试必问-JVM性能调优 │ JVM性能调优 2018-10-25.mp4 │ ├─面试必问-mybaits源码分析 │ │ 鲁班学院-上课笔记mybaits源码分析9-05.docx │ │ │ └─mybaits源码分析 │ mybaits源码分析.mp4 │ ├─面试必问-springcloud架构微服务项目 │ springcloud架构微服务项目.mp4 │ ├─面试必问-SpringMVC源码分析 │ SpringMVC源码分析.mp4 │ ├─面试必问-webservice原理分析 │ webservice原理分析.mp4 │ ├─面试必问-使用Springboot快速搭建SSM框架 │ 使用SpringBoot快速搭建SSM框架.mp4 │ ├─面试必问-双十一系统架构之Mysql索引技术剖析 │ 双十一系统架构之Mysql索引技术剖析.mp4 │ ├─面试必问-大牛带你手写dubbo框架 │ 大牛带你手写Dubbo框架.mp4 │ ├─面试必问-实战分布式之手写分布式事务框架 │ 实战分布式之手写分布式事务框架.mp4 │ ├─面试必问-带你精通springAOP—面试无忧虑 │ 带你精通AOP——面试无忧虑.mp4 │ ├─面试必问-微服务架构深入浅出讲解springcloud │ 微服务架构 --深入浅出讲解springcloud.mp4 │ ├─面试必问-教你手写MyBatis框架 │ 一小时教你手写MyBatis框架.mp4 │ ├─面试必问-架构杀手锏——java混乱的日志体系 │ java混亂日志体系源码揭秘.mp4 │ ├─面试必问-深入微服务之SpringBoot&Docker1 │ 深入微服务之SpringBoot&Docker.mp4 │ └─面试必问-聊聊哈希算法与HashMap
Docker —— 从入门到实践 - v1.0.pdf Dubbo部署文档.doc Dubbo面试专题.docx java后端面试题答案.pdf Java并发Fork-Join框架原理解析.docx JAVA核心知识整理.pdf JAVA核心知识点整理.pdf Java面试笔记.docx JAVA面试题解惑系列.pdf Java面试题(基础).pdf JVM 实用参数系列 - v1.0.pdf JVM与性能优化知识点整理.pdf JVM面试专题.docx JVM面试专题及答案.pdf Linux系统Redis笔记.docx MongoDB学习笔记.docx mybatis原理.docx MyBatis面试专题.docx MyBatis面试专题及答案.pdf Mybatis面试题(含答案).pdf MySQL性能优化的21个最佳实践.pdf mysql面试专题.docx MySQL面试题(含答案).pdf Netty面试专题.docx Nginx实战.pdf ORACLE数据库SQL语句编写优化总结.pdf Redis面试题(含答案).docx Redis面试题(含答案).pdf solr索引搜索.docx Spring Boot实战 .pdf Spring Boot面试专题.docx Spring Cloud面试专题.docx SpringBoot面试专题及答案.pdf SpringCloud面试专题及答案.pdf SpringMVC面试专题.docx Spring全家桶知识笔记.pdf Spring源码分析.pdf Spring面试专题.docx Spring面试专题及答案.pdf Spring面试题(含答案).pdf Tomcat面试专题.docx vivo2021届春季招聘在线编程考试.docx 哔哩哔哩2021招聘后端笔试卷(一).docx 哔哩哔哩2021招聘后端笔试卷(二).docx 多线程面试59题(含答案).pdf 多线程面试专题及答案.pdf 完整面试题(含答案).pdf 小米2021软件开发工程师笔试题一.docx 小米2021软件开发工程师笔试题二.docx 并发编程面试专题.docx 微服务面试专题及答案.pdf 快手2021招聘秋招笔试--工程A试卷.docx 快手2021招聘秋招笔试--工程B试卷.docx 快手2021招聘秋招笔试--工程C试卷.docx 拼多多2021部分编程题合集.docx 数据库面试专题及答案.pdf 正则表达式.pdf 消息中间件面试专题及答案.pdf 深入浅出Redis.pdf 爱奇艺2021Java方向笔试题.docx 爱奇艺2021Java方向笔试题(第一场).docx 看透springMvc源代码分析与实践.pdf 网易2021招聘笔试- 系统开发研发工程师(提前批).docx 网易2021笔试- Java 开发工程师(提前批).docx 网易2021笔试- Java 开发工程师(正式批).docx 网易2021笔试- 系统开发研发工程师(正式批).docx 美团点评2021校招后台开发方向笔试题.docx 美团点评2021系统开发方向笔试题.docx 腾讯2021招聘-后台.docx 设计模式面试专题及答案.pdf 详细解析_Log4j_配置 .docx 阿里百度美团面试题集合.pdf 面试宝典.pdf

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值