第一章 并发编程的挑战

1. 单核CPU分配运行时间给各个线程,实现多线程执行代码。

举例:看英文书时某个单词不会,先记住看到书的页数和行数,然后去查单词,查完回到看书状态,相当于上下文切换。

2. 多线程并不一定比串行执行代码快

举例:循环累加的代码,count=10000时,并发比串行执行慢,因为上下文切换有额外开销

public class ConcurrencyTest {private static final long count = 10000l;public static void main(String[] args) throws InterruptedException {concurrency();serial();}private static void concurrency() throws InterruptedException {long start = System.currentTimeMillis();Thread thread = new Thread(new Runnable() {@Overridepublic void run() {int a = 0;for (long i = 0; i < count; i++) {a += 5;}System.out.println(a);}});thread.start();int b = 0;for (long i = 0; i < count; i++) {b--;}thread.join();long time = System.currentTimeMillis() - start;System.out.println("concurrency :" + time + "ms,b=" + b);}private static void serial() {long start = System.currentTimeMillis();int a = 0;for (long i = 0; i < count; i++) {a += 5;}int b = 0;for (long i = 0; i < count; i++) {b--;}long time = System.currentTimeMillis() - start;System.out.println("serial:" + time + "ms,b=" + b + ",a=" + a);}
}
3. 减少上下文切换的方法有无锁并发编程、CAS算法、使用最少线程和使用协程。
  • 无锁并发编程。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。
  • CAS算法。Java的Atomic包使用CAS算法来更新数据,而不需要加锁。
  • 使用最少线程。避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。
  • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。
4. 减少上下文切换实战

这里我本地没有尝试,用jstack检查线程信息,发现很多线程处于waiting状态。

5. 死锁实例
public class DeadLockDemo {private static String A = "A";private static String B = "B";public static void main(String[] args) {new DeadLockDemo().deadLock();}private void deadLock() {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (A) {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (B) {System.out.println("1");}}}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (B) {synchronized (A) {System.out.println("2");}}}});t1.start();t2.start();}}

避免死锁的方法

  • 避免一个线程同时获取多个锁。
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
  • 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。”

定时锁lock.tryLock(timeout)使用参考如下代码
如果获取锁失败,就不要一直等着获取了,进入else的代码,或者在else代码中重新尝试获取。

public class LockTest {private Lock lock = new ReentrantLock();//需要参与同步的方法private void method(Thread thread){if(lock.tryLock()){try {System.out.println("线程名"+thread.getName() + "获得了锁");}catch(Exception e){e.printStackTrace();} finally {System.out.println("线程名"+thread.getName() + "释放了锁");lock.unlock();}}else{System.out.println("我是"+Thread.currentThread().getName()+"有人占着锁,我就不要啦"); }}public static void main(String[] args) {LockTest lockTest = new LockTest();//线程1Thread t1 = new Thread(()->{lockTest.method(Thread.currentThread());}, "t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {lockTest.method(Thread.currentThread());}}, "t2");t1.start();t2.start();}}

第二章 Java并发机制的底层实现原理

  1. volatile关键字
    保证线程间对修饰变量的可见性。在当前线程要修改该变量时,CPU需要从工作内存写回到主内存,其他CPU保存的该变量缓存失效。在汇编中增加了lock。在多CPU开发中,volatile用的好可以代替synchronized,执行成本更低。
    Java并发 volatile关键字

  2. synchronized关键字
    Synchronized锁 偏向锁 锁优化

  3. CAS原子操作实现原理

  • Intel处理器
    内存模型如下图所示1
  • Java角度
    原子类

第三章 Java内存模型

并发编程的艺术 volatile关键字 JMM 重排序

第四章 Java并发编程基础

“现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个Java程序,操作系统就会创建一个Java进程。现代操作系统调度的最小单元是线程,也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。”
Excerpt From: 方腾飞,魏鹏,程晓明 著. “Java并发编程的艺术.” Apple Books.

  • 线程优先级
    证明jdk8忽略线程优先级的例子
/*** 6-2* 证明设置了jdk8忽略了线程优先级*/
public class Priority {private static volatile boolean notStart = true;private static volatile boolean notEnd   = true;public static void main(String[] args) throws Exception {List<Job> jobs = new ArrayList<Job>();for (int i = 0; i < 10; i++) {int priority = i < 5 ? Thread.MIN_PRIORITY : Thread.MAX_PRIORITY;Job job = new Job(priority);jobs.add(job);Thread thread = new Thread(job, "Thread:" + i);thread.setPriority(priority);thread.start();}notStart = false; //此处的作用是让前面的所有线程都休眠,时间未知,等待CPU唤起,notStart指定下达后再一起跑Thread.currentThread().setPriority(8);System.out.println("done.");TimeUnit.SECONDS.sleep(10);notEnd = false;for (Job job : jobs) {System.out.println("Job Priority : " + job.priority + ", Count : " + job.jobCount);}}static class Job implements Runnable {private int  priority;private long jobCount;public Job(int priority) {this.priority = priority;}public void run() {while (notStart) {Thread.yield();}while (notEnd) {Thread.yield();jobCount++;}}}
}
  • 线程状态

整理见这篇博客 线程的状态

  • 中断线程

“线程通过检查自身是否被中断来进行响应,线程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()对当前线程的中断标识位进行复位。如果该线程已经处于终结状态,即使该线程被中断过,在调用该线程对象的isInterrupted()时依旧会返回false。

从Java的API中可以看到,许多声明抛出InterruptedException的方法(例如Thread.sleep(long millis)方法)这些方法在抛出InterruptedException之前,Java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException,此时调用isInterrupted()方法将会返回false。”

Excerpt From: 方腾飞,魏鹏,程晓明 著. “Java并发编程的艺术.” Apple Books.

public class Interrupted {public static void main(String[] args) throws Exception {Thread sleepThread = new Thread(new SleepRunner(), "SleepThread");sleepThread.setDaemon(true);Thread busyThread = new Thread(new BusyRunner(), "BusyThread");busyThread.setDaemon(true);sleepThread.start();busyThread.start();TimeUnit.SECONDS.sleep(5);sleepThread.interrupt();busyThread.interrupt();System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted());System.out.println("BusyThread interrupted is " + busyThread.isInterrupted());TimeUnit.SECONDS.sleep(2);}static class SleepRunner implements Runnable {@Overridepublic void run() {while (true) {try {Thread.sleep(10000);} catch (InterruptedException e) {System.out.println("interruptedException");}//SleepUtils.second(10);}}}static class BusyRunner implements Runnable {@Overridepublic void run() {while (true) {}}}
}
  • 优雅的终止线程
    建议用第二种方法

“示例在执行过程中,main线程通过中断操作和cancel()方法均可使CountThread得以终止。这种通过标识位或者中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地将线程停止,因此这种终止线程的做法显得更加安全和优雅。”

Excerpt From: 方腾飞,魏鹏,程晓明 著. “Java并发编程的艺术.” Apple Books.

public class Shutdown {public static void main(String[] args) throws Exception {Runner one = new Runner();Thread countThread = new Thread(one, "CountThread");countThread.start();// “睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束”TimeUnit.SECONDS.sleep(1);countThread.interrupt();Runner two = new Runner();countThread = new Thread(two, "CountThread");countThread.start();// “睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束”TimeUnit.SECONDS.sleep(1);two.cancel();}private static class Runner implements Runnable {private long             i;private volatile boolean on = true;@Overridepublic void run() {while (on && !Thread.currentThread().isInterrupted()) {i++;}System.out.println("Count i = " + i);}public void cancel() {on = false;}}}
  • 线程间通信
    线程间通信

  1. https://blog.csdn.net/java1993666/article/details/77880651 ↩︎

并发编程的艺术 读书笔记相关推荐

  1. JAVA并发编程的艺术-读书笔记

    1.并发编程的挑战 多线程并不一定能带来性能提升,相反过多的线程导致线程创建和上下文切换有时会比单线程性能更低 无锁并发编程:根据数据id进行取模,不同的线程处理不同段的数据 死锁:资源互相等待,线程 ...

  2. 《Java并发编程的艺术》笔记

    <Java并发编程的艺术>笔记 第1章 并发编程的挑战 1.1 上下文切换 CPU通过时间片分配算法来循环执行任务,任务从保存到再加载的过程就是一次上下文切换. 减少上下文切换的方法有4种 ...

  3. 《Java 并发编程实战》--读书笔记

    Java 并发编程实战 注: 极客时间<Java 并发编程实战>–读书笔记 GitHub:https://github.com/ByrsH/Reading-notes/blob/maste ...

  4. Java并发编程的艺术-阅读笔记和思维导图

    最近在坚持每天阅读<<Java并发编程的艺术>>,不但做好笔记(MarkDown格式),还做好思维导图. 如果大家感兴趣,可以可以到码云上阅读笔记和到ProcessOn上阅读思 ...

  5. 《Java并发编程实践》读书笔记

    http://macrochen.iteye.com/blog/660796 并发编程在编写高性能, 可伸缩应用的时候经常用到的一项技术, 也是相对来说比较高级的一项技术, 是每一个做后端开发的必备技 ...

  6. [转] 《Java并发编程的艺术》笔记

    转自https://gitee.com/Corvey/note 作者:Corvey 第一章 并发编程的挑战 略 第二章 Java并发机制的底层实现原理 volatile的两条实现原则: Lock前缀指 ...

  7. 《C++并发编程实战》读书笔记——chapter 3_线程间共享数据

    更多的阅读笔记,及示例代码见 Github https://github.com/anlongstory/C-_Concurrency_in_Action_reading_notes 本章主要内容: ...

  8. 《Java并发编程实战》读书笔记

    Subsections  线程安全(Thread safety) 锁(lock) 共享对象 对象组合 基础构建模块 任务执行 取消和关闭 线程池的使用 性能与可伸缩性 并发程序的测试 显示锁 原子变量 ...

  9. 竞态条件的赋值_《Java并发编程实战》读书笔记一:基础知识

    一.线程安全性 一个对象是否是需要是线性安全的,取决于它是否需要被多个线程访问 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要额外的同步,这个 ...

最新文章

  1. python第三方库排行-scikit-learn: Python强大的第三方库
  2. vs unity shader插件_一些Shader资料
  3. vscode 智能打印_vscode智能提示
  4. imagereader java_java中ImageReader和BufferedImage获取图片尺寸实例
  5. gin 编译路径错误_[系列] Gin框架 - 自定义错误处理
  6. android ndk 在project中加入引入dll,在Android-Studio中导入“预建库”(NDK支持)
  7. 通过Chrome扩展来批量复制知乎好友
  8. 编译OpenCV:cv2.cpp:23:33: fatal error: numpy/ndarrayobject.h: 没有那个文件或目录
  9. PreferenceActivity使用介绍
  10. 信号处理电路整理(RC、运放、TTL)
  11. 乐鑫再次称王WiFi MCU市场
  12. 字体识别在线工具-整理
  13. 大平原顾问快讯FRX移至新服务器
  14. php微信昵称保存,附件十四 存储微信昵称的处理方法
  15. 删除右键新建多余菜单
  16. 苹果cms10好看的模板自适应高端大气免费模板
  17. linux程序接口实验,实验 一 操作系统接口实验
  18. mysql 创建连接报错_创建数据库连接报错 · Issue #IRK5Z · 一米一粟信息科技/kooteam - Gitee.com...
  19. 手搭深度推荐模型(四) NFM
  20. 苹果X可以升级5G吗_苹果分析师:2020 年的 iPhone 屏幕尺寸升级,还有 5G

热门文章

  1. 【初级程序员】的十点提升建议,以及10个【程序员】的好习惯,让你“秃”然变强
  2. 操作系统进程调度算法——吸烟者问题
  3. 如何自建obs服务器,使用 Nginx+OBS 搭建rmtp直播服务器并进行直播
  4. Exchange 2013管理员账号登录ecp登陆不了
  5. 关于debug时的断点无效问题 [已解决,不知原因]
  6. 每日思考第 70 期:人,往往会低估一件事的难度,而高估自己的能力
  7. java 基本数据类型的默认初始值
  8. 蚂蚁的愤怒之源-上篇
  9. 1.初接触思科模拟器
  10. 《让子弹飞》系列——张麻子的斗争策略