引子:关于Java并发中的volatile关键字

并切--定义

悲观锁:

1、一个线程在执行一个操作时持有对一个资源的独占锁(A线程占了资源a,则其他线程就不能操作资源a)

2、一般用在冲突比较可能发生的场景下

乐观锁:

1、尝试采用原子操作,而不需要持有锁;冲突可被检测,如果发生冲突,具有相应的重试逻辑。(不持有锁)

2、通常用在冲突比较少发生的场景下

非阻塞算法

1. 算法确保对线程间竞争共享资源时,不会因为互斥而使任一线程的执行无限延迟

无锁算法

1、如果系统整个流程的执行是无阻塞的(系统某一部分可能被短暂阻塞),这种非阻塞算法就是无锁的。

2、无锁算法比传统的基于锁算法对系统开销更小,且更容易在多核多CPU上扩展;

3、在实时系统中可以避免锁带来的延迟

4、CAS或LL/SC,以及内存屏障相关的指令经常被用在算法实现中

无等待算法

1、如果每个线程的执行都是无阻塞的,这种非阻塞算法就是无等待的(比无锁算法更好)

关于JAVA里面的并发

1、JAVA的内存模型并不保证一个线程可以一直以程序执行的顺序看到另一个线程对变量的修改,除非两个线程都跨越了同一个内存屏障。(safe publication)

Java内存模型

代码顺序规则

1、一个线程内的每个动作happens-before同一个线程内在代码顺序上在其后的所有动作

volatile变量规则

1. 对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入

我理解就是跨线程的变量读写,即A线程对变量的修改,B线程可以读到。?

传递性

1. 如果A happens-before B,B happens-before C,那A happens-before C

public class VolatileExample {int x = 0;volatile int b = 0;private void write() {x = 5;b = 1;}private void read() {int dummy = b;while (x != 5) {}}public static void main(String[] args) throws Exception {final VolatileExample example = new VolatileExample();Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {example.write();}});Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {example.read();}});thread1.start();thread2.start();thread1.join();thread2.join();}
}

1、根据代码顺序规则,线程1的x=5; happens-before b=1;; 线程2的int dummy = b; happens-before while(x!=5);
2、根据volatile变量规则,线程2的b=1; happens-before int dummy=b;

3、根据传递性,x=5; happens-before while(x!=5);

二)介绍下AtomicBoolean,AtomicInteger等这些Atomic*的类

atomic class可以帮助我们来简化同步处理。它的基本工作原理:使用了同步synchronized的方法来实现对一个long,integer,对象的增、减、赋值(更新)操作。比如对++运算操作,AtomicInteger可以将它持有的Integer能够atomic地递增。在需要访问两个或两个以上atomic变量的程序代码,通常需要被synchronize以便两者操作能够被当作是一个atomic的单元。

简单理解就是:对一个atomic类型的变量,多线程环境下对它进行增加或减少操作,不会出现线程问题. 这些比较原生的数据类型是线程安全的,而且支持无阻塞无锁定。

为了提升性能,atomic*并没有使用synchronized关键字,而是直接使用了最底层(本地C语言实现代码)来完成。

来看看在多线程环境下如果对同一个变量有同时操作的时候,atomic*的变量与非这种类型的变量会有什么处理上的差异?

三)关于CountDownLatch这个关键字

这是个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完成再执行。

常用的场景是:主线程去等待其子线程们都完成了各自的任务之后再继续往下走。比如主线程是一个主的工作流,子线程里面是各个执行器任务执行线程,主线程得等到子线程都完成了各自的任务之后才能往下执行。

实现原理:它是通过一个计数器来实现的,计数器初始值是线程的数量,当一个线程完成了自己的任务后,计数器的值就会减1.当计数器值到达0,它表示全部的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

伪代码:

1. 主线程开启

2. 创建CountDownLatch

3. 开启N个子线程

4. 主线程在等

5. N个子线程都结束掉了

6. 主线程恢复继续

它的构造函数里面的参数就是要等待的线程数量,这个值只能设置一次,而且不能重设。

主线程必须要在启动其他线程之后立即调用.await()方法。这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自任务。

子线程一旦完成要通知countDownLatch对象,通知的方法就是调用CountDownLatch.countDown方法来完成,每调一次count就会减少1,所以当这个N个线程都调了count就会变0.(如果子线程抛异常没调到,就会一直阻塞主线程。)

最常见的场景:

1、实现最大的并行性:

如果想同时启N个线程,实现最大程度的并行性。

2、开始执行前等待N个线程先完成各自任务

比如在提供服务之前要确保N个服务都OK了再提供服务

3、死锁检测

可以使用N个线程访问共享资源

测试的一个示例:(看看里面的设计模式,抽象类的使用这块值得借鉴)

import java.util.concurrent.CountDownLatch;/*** */
public abstract class BaseHealthChecker implements Runnable {private CountDownLatch _latch;private String _serviceName;private boolean _serviceUp;public BaseHealthChecker(String serviceName, CountDownLatch latch) {super();this._latch = latch;this._serviceName = serviceName;this._serviceUp = false;}@Overridepublic void run() {try {verifyService();_serviceUp = true;} catch (Throwable t) {t.printStackTrace();_serviceUp = false;} finally {if (_latch != null) {_latch.countDown();}}}public String getServiceName() {return _serviceName;}public boolean isServiceUp() {return _serviceUp;}public abstract void verifyService();
}

抽象的一个父类,有一个共同的方法叫run(). 然后各个子健康检查方式复用这个框架。

public class NetworkHealthCheck extends BaseHealthChecker {public NetworkHealthCheck(CountDownLatch latch) {super("Network Service", latch);}@Overridepublic void verifyService() {System.out.println("开始检查网络是否OK" + this.getServiceName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(this.getServiceName() + " is UP");}
}

框架的意义:把公用的部分放到抽象父类里面去。子类写自己不一样的抽象方法。(比如我们可以写一个通用的执行器父类)

再加一个执行器

public class CacheHealthCheck extends BaseHealthChecker {public CacheHealthCheck(CountDownLatch latch) {super("Cache Service", latch);}@Overridepublic void verifyService() {System.out.println("开始检查缓存是否OK" + this.getServiceName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(this.getServiceName() + " is UP");}
}

执行代码

public class ApplicationStartupUtil {private static List<BaseHealthChecker> _services;private static CountDownLatch _latch;private ApplicationStartupUtil() {}private final static ApplicationStartupUtil INSTANCE = new ApplicationStartupUtil();public static ApplicationStartupUtil getInstance() {return INSTANCE;}public static boolean checkExternalServices() throws InterruptedException {_latch = new CountDownLatch(2);_services = new ArrayList<BaseHealthChecker>();_services.add(new NetworkHealthCheck(_latch));_services.add(new CacheHealthCheck(_latch));Executor executor = Executors.newFixedThreadPool(_services.size());for (final BaseHealthChecker v : _services) {executor.execute(v);}_latch.await();for (final BaseHealthChecker v : _services) {if (!v.isServiceUp()) {return false;}}return true;}public static void main(String[] args) throws InterruptedException {boolean result = ApplicationStartupUtil.checkExternalServices();System.out.println(result);}
}

[Jdk源码学习]聊聊concurrent包下面的volite*相关推荐

  1. JDK源码学习之一lang包

    JAVA编程思想,第一章,对对象的概念,以及访问权限的有了深入理解 JDK,看到Math Character类的of方法,这里用到了二分查找法 public static UnicodeBlock o ...

  2. 【JDK源码】java.lang包常用类详解

    接下来的几天开始JDK源码的学习和总结,之前看<java编程思想>的时候看到java的基础知识有很多,其中支撑着这些基础的基础中的基础当属JDK.JDK的基础代码里面又分了很多基础的模块, ...

  3. JDK源码学习-基础

    JDK源码学习 目录 基础 1. 安装 1.1 下载JDK 1.2 配置环境变量 1.3 验证 2. 简单的程序 2.1 编写代码 2.2 编译文件 2.3 执行类 3. java基本类型 基础 1. ...

  4. JAVA JDK 源码学习

    JAVA JDK 源码学习 ,以1.8为例,按照下面图片顺序依次学习: applet ,awt,beans,io,lang,math,net,nio,rmi,security,sql,text,tim ...

  5. JDK源码学习笔记——Integer

    一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...

  6. 【JDK源码】java.io包常用类详解

    看完java.io的JDK源码,在网上发现一篇关于java.io中的类使用的文章总结的很全面,看完之后在原文的基础上加了一些自己的总结如下构成了本篇文章.原文地址 一.Java Io流 1. Java ...

  7. JDK源码学习路线~每天学一点~每天进步一点点

    很多java开发的小伙伴都会阅读jdk源码,然而确不知道应该从哪读起.以下为小编整理的通常所需阅读的源码范围. 标题为包名,后面序号为优先级1-4,优先级递减 1.java.lang 1) Objec ...

  8. 非常实用,IDEA 搭建JDK源码学习环境(可修改+断点+笔记)

    点击关注公众号,实用技术文章及时了解 来源:chenxiao.blog.csdn.net/article/details/104369824 在学习JDK源码的时候,自然少不了代码的调试. 阅读与调试 ...

  9. JDK源码学习笔记——String

    1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() {i ...

最新文章

  1. vim 7.4同时支持python 2.x和3.x问题调研
  2. CCommandLineInfo类
  3. 丁磊旗下首个IPO的AI业务!网易有道年入7个亿,清华96计算机系为主力,沈向洋任董事...
  4. php ajax 点击后刷新当前页面,ajax请求值后返回会刷新页面?
  5. matlab数字图像处理函数,MATLAB数字图像处理学习(二)|常用函数
  6. TensorFlow12CIFAR-CNN实现
  7. ISO/IEC 27701:2019(隐私信息安全管理扩展要求和指南)解读(二)
  8. his系统计算机软件,医学系统(一)医院常用的软件系统:PACS系统、HIS系统、RIS系统、LIS系统、CIS系统...
  9. 中兴笔试题,求子区间元素运算后结果为0
  10. CSDN博客新增「评论置顶」、「定时发布」功能,翘首期盼的PC版「每日一练」上线!【第14期*2021.9.22】
  11. wine: /home/cpr/.wine is not owned by you
  12. 江苏专转本计算机第七章,2013江苏专转本 第七章 无穷级数.ppt
  13. 手机摄影-参数(快门)
  14. 『Python』Excel文件的读取以及DataFrame的相关操作 (2)
  15. 阴阳师自动御魂觉醒超鬼王脚本
  16. SQL:请用sql实现学生表中出现的学生姓名次数最多的学生姓名?
  17. 普元 AppServer 7.0 服务器的日志在哪查看?
  18. 搭建博客、自己的小窝?快来看看这些开源静态网站生成器
  19. 利用图像内插法放大缩小图像 Matlab
  20. android 多个手指同时点击,处理多点触控手势  |  Android 开发者  |  Android Developers...

热门文章

  1. 阿里云负载均衡SLB,HTTPS动态网站部署负载均衡,实现高并发流量分发
  2. 两个经纬度之间距离和角度的计算
  3. 自定义实现HashMap(二)
  4. 嵌入式C语言自我修养 (04):Linux 内核第一宏:container_of
  5. android AlertDialog的基本属性以及创建方法
  6. 程序员工作三年:跳槽三家公司,从阿里出来,工资从15K涨到45K?
  7. 攻破算法题_散列_PAT_B1029_到底买不买(20)
  8. 【vue中实现iframe 自适应高度和去除滚动条】
  9. Html透明滚动条,透明滚动条 HTML
  10. 【C++】getline函数用法