Rhythmli's blog Rhythmli's blog
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Rhythmli

知识就是财富
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

  • GitHub技巧

  • Nodejs

  • 博客搭建

  • CSDN迁移

    • Spring IOC
    • Ngnix 阿里云
    • 最长回文子串
    • 面试题xuexixiexue
    • 哈希集合和哈希映射的简单设计
    • leetcode2021.11.03
    • Leetcode2021.11.2
    • JUC学习
    • 869. 重新排序得到 2 的幂
    • Java自动装箱拆箱
    • 55. 跳跃游戏
    • 剑指 Offer II 085. 生成匹配的括号
    • 300. 最长递增子序列
    • Java并发编程之美 01
      • Java并发编程之美
        • 01.多线程实现的方式
        • 01.继承Thread,重写run方法
        • 02.实现Runnable接口的run方法
        • 02.进程通知等待
        • 01.wait()函数
        • 02.notify() 函数
        • 03.notifyAll() 方法
        • 04.join 方法
        • 05.sleep 方法
        • 06.yield 方法
        • 07.死锁
        • 03.多线程并发
        • 01.synchronized 的内存语义
        • 02.volatile 关键字
        • 03.那么一般在什么时候才使用 volatile 关键字呢?
        • 04Java 中的原子性操作
        • 05伪共享
        • 06.锁的概述
        • 01.乐观锁与悲观锁
        • 02.公平锁与非公平锁
        • 03.独占锁与共享锁
        • 04.可重入锁
        • 05.自旋锁
    • 134. 加油站
    • 139. 单词拆分
    • 岛屿类问题题解
    • 138. 复制带随机指针的链表
    • 347. 前 K 个高频元素
    • 剑指 Offer II 026. 重排链表
    • 剑指 Offer II 025. 链表中的两数相加
    • 剑指 Offer II 014. 字符串中的变位词
    • 剑指 Offer II 010. 和为 k 的子数组
    • 剑指 Offer II 009. 乘积小于 K 的子数组
    • 剑指 Offer II 008. 和大于等于 target 的最短子数组
    • 剑指 Offer II 007. 数组中和为 0 的三个数
    • 剑指 Offer II 006. 排序数组中两个数字之和
    • 剑指 Offer II 002. 二进制加法
    • 129. 求根节点到叶节点数字之和
    • 113.路径总和 II
    • leetcode18. 四数之和
    • 编译OpenCV 以及 openc_contrib 提示缺少boostdesc_bgm.i文件出错的解决
    • fork()浅学习
    • SSM 增删改查
    • springmvc helloworld
    • Spring01 hello实验
    • 树的DFS和BFS
    • leetcode——二分法
    • Halo博客搭建
    • 计算机视觉领域的一些牛人博客,超有实力的研究机构等的网站链接---转载
    • opencv+python+OpenPose姿态实时识别
    • 03.KNN算法 李航统计学习方法
    • 02.感知机 李航统计学习方法
    • 01.最小二乘法拟合 李航统计学习方法
    • Pycharm atplotlib.pyplot图像不显示解决方法
    • 论文阅读01 SVM+kNN图像分类
    • Java简单实现计算器——用数组实现栈
    • 【剑指Offer3】无重复字符的最长子串
    • 【剑指Offer5】最长回文字符串
    • TF-IDF求取文本相似度
    • JAVA_day02
    • JAVA_day01
    • 递归产生回文数
    • 中国象棋QT登录注册以及悔棋功能
    • STM32F4学习笔记(基础介绍篇)
    • leetcode_04 递归,回溯与分治
    • leetcode03_贪心算法
    • leetcode01--链表
    • leetcode_02栈
    • Docker学习入门
    • C/C++编译与链接 程序员的自我修养:链接 装载和库
    • Nginx简单学习
    • JAVA网络编程
    • JVM初步学习
    • Spring简单学习
    • 标准项目格式
    • 设计模式中的几个原则
    • Redis和IDEA简单创建及增删改查
    • Mybatis快速入门01
    • Redis全程学习笔记(附带学习的视频教程)
    • QT入门学习中最基础的那些事儿
    • QT中文输出错误问题:C2001
    • OOP:面向对象编程
    • LINUX常用命令集合(待续)
    • 《C和指针》简单学习笔记
    • 二叉树,栈存储及遍历小程序
    • 数据结构简单学习笔记
    • 扑克牌
    • C++动态内存和智能指针
    • 设计模式之简单工厂模式
    • leetcode_01数组
    • 嵌入式Linux移植应用
    • LINUX 进程与线程 信号量 通信
  • 技术
  • CSDN迁移
梁山话事人
最新推荐文章2026-08-09
目录

Java并发编程之美 01

原文链接:https://blog.csdn.net/qq_39355828/article/details/120633032 (opens new window)

# Java并发编程之美

# 01.多线程实现的方式

# 01.继承Thread,重写run方法

public static class Mythread extends Thread
    {
        @Override
        public void run()
        {
            System.out.println("hello,Thread");
        }
    }

    public static void main(String[] args) {
        Mythread mythread=new Mythread();
        mythread.start();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13

# 02.实现Runnable接口的run方法

public static class Mythread implements Runnable
    {
        @Override
        public void run()
        {
            System.out.println("hello,Thread");
        }
    }

    public static void main(String[] args) {
        Mythread mythread=new Mythread();
        new Thread(mythread).start();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13

# 02.进程通知等待

# 01.wait()函数

当一个线程调用一个共享变量的 wait() 方法时,该调用线程会被阻塞挂起,直到发生 下面几件事情之一才返回:(1)其他线程调用了该共享对象的 notify() 或者 notifyAll() 方法; (2)其他线程调用了该线程的 interrupt() 方法

另外需要注意的是, 一个线程可以从挂起状态变为可以运行状态(也就是被唤醒), 即使该线程没有被其他线程调用 notify()、notifyAll() 方法进行通知,或者被中断,或者等待超时,这就是所谓的虚假唤醒。

# 02.notify() 函数

一个线程调用共享对象的 notify() 方法后,会唤醒一个在该共享变量上调用 wait 系列 方法后被挂起的线程。一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线 程是随机的。
1

# 03.notifyAll() 方法

会唤醒所有在该共享变量上由于调用 wait 系列方法而被挂起的线程。

# 04.join 方法

等待线程执行终止. join 方法是 Thread 类直接提供的。join 是无参且返回值为 void 的方法。

# 05.sleep 方法

当一个执行中的线程调用了 Thread 的 sleep 方 法后,调用线程会暂时让出指定时间的执行权,也就是在这期间不参与 CPU 的调度,但 是该线程所拥有的监视器资源,比如锁还是持有不让出的。

# 06.yield 方法

Thread 类中有一个静态的 yield 方法,当一个线程调用 yield 方法时,实际就是在暗示 线程调度器当前线程请求让出自己的 CPU 使用,

# 07.死锁

互斥条件 :指线程对已经获取到的资源进行排它性使用,即该资源同时只由一个线程占用。如果此时还有其他线程请求获取该资源,则请求者只能等待,直至占有资 源的线程释放该资源。

请求并持有条件:指一个线程已经持有了至少一个资源,但又提出了新的资源请求, 而新资源已被其他线程占有,所以当前线程会被阻塞,但阻塞的同时并不释放自己已经获取的资源。

不可剥夺条件 :指线程获取到的资源在自己使用完之前不能被其他线程抢占,只有在自己使用完毕后才由自己释放该资源。

环路等待条件 :指在发生死锁时,必然存在一个线程—资源的环形链,即线程集合 {T0,T1,T2,…,T n } 中的 T0 正在等待一个 T1 占用的资源,T1 正在等待 T2 占 用的资源,……T n 正在等待已被 T0 占用的资源。

死锁的避免:

破坏请求并持有和环路等待条件

资源申请的有序性

# 03.多线程并发

多线程并发导致的问题,从根本上说是主内存和工作内存的矛盾,线程工作的时候会把把工作内存复制到自己工作内存进行,从而刷新回会主内存时候的不一致性。

# 01.synchronized 的内存语义

进入 synchronized 块的内存语义是把在 synchronized 块内使用到的变量从线程的工作内存 中清除,这样在 synchronized 块内使用到该变量时就不会从线程的工作内存中获取,而是 直接从主内存中获取。退出 synchronized 块的内存语义是把在 synchronized 块内对共享变 量的修改刷新到主内存。

# 02.volatile 关键字

可以确保对一个变量的更新对其他线程马上可见。当一个变量被声明为 volatile 时,线程在写入变量时不会把值缓存在寄存器或者 其他地方,而是会把值刷新回主内存。当其他线程读取该共享变量时,会从主内存重新获 取最新值,而不是使用当前线程的工作内存中的值。volatile 的内存语义和 synchronized 有 相似之处,具体来说就是,当线程写入了 volatile 变量值时就等价于线程退出 synchronized 同步块(把写入工作内存的变量值同步到主内存),读取 volatile 变量值时就相当于进入同 步块(先清空本地内存变量值,再从主内存获取最新值)。

# 03.那么一般在什么时候才使用 volatile 关键字呢?

(1)写入变量值不依赖变量的当前值时。因为如果依赖当前值,将是获取—计算—写入三步操作,这三步操作不是原子性的,而 volatile 不保证原子性。

(2)读写变量值时没有加锁。因为加锁本身已经保证了内存可见性,这时候不需要把变量声明为 volatile 的。

# 04Java 中的原子性操作

(1)synchronized

使用 synchronized 关键字的确可以实现线程安全性, 即内存可见性和原子性, 但是 synchronized 是独占锁,没有获取内部锁的线程会被阻塞掉,而这里的 getCount 方法只是 读操作,多个线程同时调用不会存在线程安全问题。但是加了关键字 synchronized 后,同 一时间就只能有一个线程可以调用,这显然大大降低了并发性。

(2)CAS

在 Java 中, 锁在并发处理中占据了一席之地, 但是使用锁有一个不好的地方, 就是当一个线程没有获取到锁时会被阻塞挂起, 这会导致线程上下文的切换和重新调度开销。Java 提供了非阻塞的 volatile 关键字来解决共享变量的可见性问题, 这在一定程度 上弥补了锁带来的开销问题, 但是 volatile 只能保证共享变量的可见性, 不能解决读改—写等的原子性问题。CAS 即 Compare and Swap, 其是 JDK 提供的非阻塞原子性操 作,它通过硬件保证了比较—更新操作的原子性。

对CAS的理解,CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

CPU去更新一个值,但如果想改的值不再是原来的值,操作就失败,因为很明显,有其它操作先改变了这个值。)

就是指当两者进行比较时,如果相等,则证明共享数据没有被修改,替换成新值,然后继续往下运行;如果不相等,说明共享数据已经被修改,放弃已经所做的操作,然后重新执行刚才的操作。容易看出 CAS 操作是基于共享数据不会被修改的假设,采用了类似于数据库的commit-retry 的模式。当同步冲突出现的机会很少时,这种假设能带来较大的性能提升。

# 05伪共享

把多个变量存放到一个 Cache 行中。当多个线程同时修改一个缓存行里面的多个变量时, 由于同时只能有一个线程操作缓存行,所以相比将每个变量放到一个缓存行,性能会有所下降

伪共享的产生是因为多个变量被放入了一个缓存行中,并且多个线程同时去写入缓存 行中不同的变量。那么为何多个变量会被放入一个缓存行呢?其实是因为缓存与内存交换 数据的单位就是缓存行,当 CPU 要访问的变量没有在缓存中找到时,根据程序运行的局部性原理,会把该变量所在内存中大小为缓存行的内存放入缓存行。

如何避免伪共享

JDK 8 提供了一个 sun.misc.Contended 注解,用来解决伪共享问题。将上面代码修改 为如下。

# 06.锁的概述

# 01.乐观锁与悲观锁

悲观锁指对数据被外界修改持保守态度,认为数据很容易就会被其他线程修改,所以 在数据被处理前先对数据进行加锁,并在整个数据处理过程中,使数据处于锁定状态。

乐观锁是相对悲观锁来说的,它认为数据在一般情况下不会造成冲突,所以在访问记 录前不会加排它锁,而是在进行数据提交更新时,才会正式对数据冲突与否进行检测。

# 02.公平锁与非公平锁

公平锁表示线程获取锁 的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。

# 03.独占锁与共享锁

独占锁保证任何时候都只有一个线程能得到锁,ReentrantLock 就是以独占方式实现 的。共享锁则可以同时由多个线程持有,例如 ReadWriteLock 读写锁,它允许一个资源可 以被多线程同时进行读操作。

# 04.可重入锁

当一个线 程再次获取它自己已经获取的锁时是否会被阻塞呢 ? 如果不被阻塞,那么我们说该锁是可重入的,也就是只要该线程获取了该锁,那么可以无限次数(在高级篇中我们将知道,严 格来说是有限次数)地进入被该锁锁住的代码。

# 05.自旋锁

当一个线程在获取锁(比 如独占锁)失败后,会被切换到内核状态而被挂起。当该线程获取到锁时又需要将其切换 到内核状态而唤醒该线程。而从用户状态切换到内核状态的开销是比较大的,在一定程度 上会影响并发性能。自旋锁则是,当前线程在获取锁时,如果发现锁已经被其他线程占有, 它不马上阻塞自己,在不放弃 CPU 使用权的情况下,多次尝试获取(默认次数是 10,可 以使用 -XX:PreBlockSpinsh 参数设置该值),很有可能在后面几次尝试中其他线程已经释 放了锁

编辑 (opens new window)
#JAVA#订阅专栏#查看详情#读书笔记
上次更新: 2026/08/11, 13:36:18
300. 最长递增子序列
134. 加油站

← 300. 最长递增子序列 134. 加油站→

最近更新
01
Spring IOC
03-31
02
Git修改分支名
08-11
03
CSS给table的tbody添加滚动条
06-29
更多文章>
Theme by Vdoing | Copyright © 2019-2026 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式