重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
本篇文章给大家分享的是有关如何分析java并发编程的艺术和并发编程,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
成都创新互联公司致力于互联网网站建设与网站营销,提供做网站、网站设计、网站开发、seo优化、网站排名、互联网营销、成都小程序开发、公众号商城、等建站开发,成都创新互联公司网站建设策划专家,为不同类型的客户提供良好的互联网应用定制解决方案,帮助客户在新的全球化互联网环境中保持优势。
通常我们在使用编发编程时,主要目的是为了程序能够更快的处理,但是并不是说更多的线程就一定能够让程序变得足够快,有时候太多的线程反而消耗了更多的资源,反而让程序执行得更缓慢
一.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:一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
五.线程池
线程池是我们使用得最多的线程框架,合理地使用它可以为我们带来很多好处
1.降低资源消耗,避免重复创建线程
2.提高响应速度,保持一定量的等待线程,不用每次都重新创建线程
3.提高线程的管理性
这里不得不提一下阿里的java编码规范中明确的指出了:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险,
六.并发编程实践
生产者和消费者模式
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度。在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,便有了生产者和消费者模式。
我写了一个简单的生产消费,但是并没有同步锁也没有同享变量,于是产生了消费者为-1的情况
于是我让这个变量在线程中共享,但是抛出了IllegalMonitorStateException异常,大致意思就是说抛出这个异常表明线程尝试等待一个对象的监视器或者去通知其他正在等待这个对象监视器的线程时,但是没有拥有这个监视器的所有权,这就证明了,其实对于共享变量操作时实际上只能有一个线程去操作
因为我们对锁的操作不正确,无法释放锁,所以我用ReentrantLock实现了
以上就是如何分析java并发编程的艺术和并发编程,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注创新互联行业资讯频道。