java并发编程的艺术(精华提炼)

通常我们在使用编发编程时,主要目的是为了程序能够更快的处理,但是并不是说更多的线程就一定能够让程序变得足够快,有时候太多的线程反而消耗了更多的资源,反而让程序执行得更缓慢

一.CPU的上下文切换
就算是单核CPU是能够处理多线程任务的,它只是不停的切换线程来执行,让我们感觉是多线程执行

下图是串行执行和并发执行的耗时对比

从图中我们可以看到,在数量不达到千万级的时候,串行和并行的耗时几乎差不多,这是因为线程在创建和上下文切换时需要一定的时间开销.

如何减少上下文的切换似乎是我们在并发编程时需要注意的一点.

  • 无锁编发编程,多线程并发强占锁时,会引起频繁的上下文切换,我们可以使用分段式的方法来处理数据,不同的线程处理不同段的数据,避免使用锁
  • 使用最少的线程.避免创建大量无效线程,任务量很少的情况,会让大量线程处于等待状态,浪费资源

二.多线程在java中的使用
volatile和synchronize
当我们使用volatile变量修饰时会引发的两件事:
1.当前处理器的数据会回写到系统内存
2.这个回写内存会使处理器中的这个内存地址无效化
这两件事说明了什么呢.在java中使用volatile可以让多个线程共享一个变量,在多个线程获取这个变量的时候,该线程会等待正在改变这个变量的线程释放掉才会去读取,这让我们多线程在共享这个变量时,才能保持唯一性

synchronized实现同步的基础
1.对于普通方法,锁的是当前实例对象
2.对于静态方法.锁的是当前class对象
3.对于方法块,锁的是作用域里的所有对象
通俗一点说,一个普通方法锁,其实是锁的是实例对象来调用时,针对于这个实例对象来说他是同步的,如果我们有多个这个实例对象同时调用它内部同步方法时,实际上还是并发请求,对于静态方法来说,他在java中只有一份,那么无论调用几次他都是一个同步调用,最后的方法块类似于我们的普通方法,不同的地方就是锁的作用域内的所有对象都被加锁,属于这个线程的私有变量

三.并发编程模型的两个关键问题
1.线程之间如何通信
2.线程之间如何同步
大部分时间中,我们不会关心线程之间如何通信,我们在实际编程中会遇到线程之间如何同步一个变量的问题,使用volatile来修饰一个变量是一个非常好的方法,在jdk8之后的jvm内存模型中,对象的实例是存储于本地栈,一个变量在使用volatile后,该对象实例的引用还是在本地内存中,而该对象的的变量则被拷贝到了主内存中用于共享,不在私有,简单来说,就是各个线程任然保留有这个变量的内存地址只是不同的内存地址被指向了主内存中的同一个变量

三.Java并发容器和框架
ConcurrentHashMap的实现原理与使用
ConcurrentHashMap是线程安全且高效的HashMap

ConcurrentHashMap的结构图

ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁(ReentrantLock),在ConcurrentHashMap里扮演锁的角色;HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组。Segment的结构和HashMap类似,是一种数组和链表结构。一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得与它对应的Segment锁

Java里的阻塞队列
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。

  1. LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
  2. PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
  3. DelayQueue:一个使用优先级队列实现的无界阻塞队列。
  4. SynchronousQueue:一个不存储元素的阻塞队列。
  5. LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
  6. LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

四.线程池
线程池是我们使用得最多的线程框架,合理地使用它可以为我们带来很多好处
1.降低资源消耗,避免重复创建线程
2.提高响应速度,保持一定量的等待线程,不用每次都重新创建线程
3.提高线程的管理性

这里不得不提一下阿里的java编码规范中明确的指出了:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险,

/*** 配置线程池** @return Executor线程池*/@Bean("threadPoolTaskExecutor")public ThreadPoolTaskExecutor getExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//线程池维护线程的最少数量executor.setCorePoolSize(10);//线程池维护线程的最大数量executor.setMaxPoolSize(50);//缓存队列executor.setQueueCapacity(99999);//对拒绝task的处理策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//设置线程名称executor.setThreadNamePrefix(QINGDUO_UPMS_SERVICE);//允许的空闲时间executor.setKeepAliveSeconds(60);//执行初始化executor.initialize();return executor;}

五.并发编程实践
生产者和消费者模式

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度。在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,便有了生产者和消费者模式。

我写了一个简单的生产消费,但是并没有同步锁也没有同享变量,于是产生了消费者为-1的情况

于是我让这个变量在线程中共享,但是抛出了IllegalMonitorStateException异常,大致意思就是说抛出这个异常表明线程尝试等待一个对象的监视器或者去通知其他正在等待这个对象监视器的线程时,但是没有拥有这个监视器的所有权,这就证明了,其实对于共享变量操作时实际上只能有一个线程去操作

因为我们对锁的操作不正确,无法释放锁,所以我用ReentrantLock实现了

/*** 生产者和消费者,ReentrantLock的实现**/
public class Test1 {private static Integer count = 0;private static final Integer FULL = 10;//创建一个锁对象private Lock lock = new ReentrantLock();//创建两个条件变量,一个为缓冲区非满,一个为缓冲区非空private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();public static void main(String[] args) {Test1 test2 = new Test1();new Thread(test2.new Producer()).start();new Thread(test2.new Consumer()).start();new Thread(test2.new Producer()).start();new Thread(test2.new Consumer()).start();new Thread(test2.new Producer()).start();new Thread(test2.new Consumer()).start();new Thread(test2.new Producer()).start();new Thread(test2.new Consumer()).start();}class Producer implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {Thread.sleep(3000);} catch (Exception e) {e.printStackTrace();}//获取锁lock.lock();try {while (count == FULL) {try {notFull.await();} catch (InterruptedException e) {e.printStackTrace();}}count++;System.out.println(Thread.currentThread().getName()+ "生产者生产,目前总共有" + count);//唤醒消费者notEmpty.signal();} finally {//释放锁lock.unlock();}}}}class Consumer implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {Thread.sleep(3000);} catch (InterruptedException e1) {e1.printStackTrace();}lock.lock();try {while (count == 0) {try {notEmpty.await();} catch (Exception e) {e.printStackTrace();}}count--;System.out.println(Thread.currentThread().getName()+ "消费者消费,目前总共有" + count);notFull.signal();} finally {lock.unlock();}}}}
}

java并发编程的艺术和并发编程这一篇就够了相关推荐

  1. Java 使用itextPdf7操作pdf,写入照片这一篇就够了

    Java 使用itextPdf7操作pdf,写入照片这一篇就够了 1. 效果图 1.1 M*N列图片(无边界&有边界) 1.2 图片重叠 1.3 文字背景图片 1.4 图片与文字相邻 & ...

  2. Java并发编程的艺术(一)——并发编程需要注意的问题

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/qq_34173549/article/details/79612496 并发是为了提升程序的执行速度 ...

  3. 【并发编程的艺术】并发机制原理

    java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化成汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令 更好的进行 ...

  4. 01Java并发编程的艺术之并发编程的挑战

    一.上下文切换 1.并发编程真的快吗?什么是上下文切换? 答案是不一定,根据测试结果,当数据小于百万的时候并发并没有串行快,这是为什么那?单核处理器的多线程并发,其实就是CPU个每个线程分配时间片,当 ...

  5. 高并发必学的 CAS 操作,看这篇就够了!

    大家好,我是树哥. CAS 操作是高并发场景下,性能如此之高的一个重要优化.今天推荐胜哥的一篇关于 CAS 的文章,带你了解 CAS 的前世今生,写得真是太棒了! 背景 在高并发的业务场景下,线程安全 ...

  6. 编程神器 VS Code,只要这一篇就够了!

    摘要:本文从VS Code的安装和简单使用开始,剖析VS Code内在特点,以及C/C++环境为例简要介绍配置方法,最后进一步介绍了远程开发环境,及VS Code远程开发环境的配置方法. 目录 1.1 ...

  7. java实训报告心得体会,看这一篇就够了!

    阶段一:筑基 Java基础掌握不牢,对于一个开发人员来说无疑是非常致命的.学习任何一个技术知识无疑不是从基础开始:在面试的时候,面试官无疑不是从基础开始拷问. 内容包括:Java概述.Java基本语法 ...

  8. 学透Java自增(++)自减(--)运算符,看这一篇就够了!

    三句重中之重的重点: 1.无论是前缀形式还是后缀形式,自增自减运算符的优先级要高于赋值运算符. 2.当一条语句中仅有++或--操作时,前缀形式与后缀形式的运算符没有任何区别. 3.前缀形式的运算规则可 ...

  9. java default修饰符_Java修饰符看这一篇就够了

    java中的修饰符分为类修饰符,字段修饰符,方法修饰符.根据功能的不同,主要分为以下几种: *权限访问修饰符(可以用来修饰类.方法和字段) public:对任何人都是可用的. protect:继承的类 ...

最新文章

  1. 给Apache虚拟主机增加端口的方法
  2. birt插件 web_Maven方式集成BIRT 4.6 Webviewer
  3. AYUSH的完整形式是什么?
  4. input搜索mysql_实现input输入时智能搜索
  5. Spark基础学习笔记09:Scala变量与数据类型
  6. 深入浅出Shell编程: Shell 变量【ZT】
  7. new image()
  8. python网络爬虫系列教程——Python+PhantomJS +Selenium组合应用
  9. 【ACL2020】tBERT: 结合主题模型和BERT实现语义相似度分析
  10. 杭电复试笔记第七天--最终篇
  11. 使用kitti2bag和RVIZ播放KITTI数据集
  12. 好程序员大数据教学点睛:Hadoop基础篇
  13. 农行2021软件开发笔试题
  14. 【论文阅读】POI2Vec: Geographical Latent Representation for Predicting Future Visitors
  15. 麻将算法c语言,[转载]麻将胡牌的检测算法
  16. 信号处理之FIR数字滤波器(Matlab仿真)
  17. 【规划】常用算法大汇总
  18. 7-2 jmu-Java-03面向对象-06-继承覆盖综合练习-Person、Student、Employee、Company (15分)
  19. 基于51单片机智能家居监控系统设计仿真(proteus仿真+源码+报告)
  20. 程序算法编程学习网站

热门文章

  1. zed2i相机中imu内参的标定及外参标定
  2. 摔玻璃球(鸡蛋)查找临界楼层
  3. Android studio EditText如何设置下划线
  4. java.lang.ClassNotFoundException: Didn't find class on path: DexPathList问题解决
  5. 机器学习中必知必会的 8 种降维技术,最后一款超硬核!
  6. niushop单商户V5.1旗舰版开源商城系统uniapp中的商业插件怎么安装
  7. MacBook Air 真香,包邮送一个!
  8. [LeetCode]495. 提莫攻击
  9. Python爬虫设计之职业社交网站——脉脉
  10. 洛谷 P2196 [NOIP1996 提高组] 挖地雷(dp简单题)