Java并发简介
年轻的时候学会了“使用”Servlet后,感觉自己什么都会做了,之后就不停的写所谓的业务逻辑,框架(这里说的不是structs,spring等,就是说servlet)给人们屏蔽了很多复杂性(更别说构建在servlet上面的那些了),极易容易上手,上手之后就一直那样了...... 随着需求的变化和复杂性的演进,开始则是在已有的知识层面上想解决办法,结果代码很复杂,毫无设计之美。绕了很多弯路才发现原来基础的东西最重要,也最难懂,学起来最没有立竿见影的效果。框架如同工具一样,是希望开发人员以最小的学习成本投入生产,但是对于一个有追求的人讲,仅仅停留在使用工具上还是比较low,我们还需要了解工具是如何工作的,如和扬长避短,甚至改造工具以达到我们特定的需求。有很多基础的东西,我们这里主要讲讲JAVA并发,同时作为自己学习的总结。
我们为什么要并发?目前很难找到单核CPU的机器了吧,同时也很少有绝对串行化的程序了吧(自己写的Hello World之类的程序除外)。 那为什么不把不同的任务(Task)同时放在不同的CPU上执行呢? 这就是要并发的原因。JAVA并发是靠线程(Thread)实现的,线程是调度的基本单元。
线程的优势
- 充分利用多CPU的能力,提高系统吞吐量:在蒸煮(任务1)的过程中,同时可以炒菜(任务2)。当然你的煤气灶是单灶就没法了。。。即使在单CPU的机器上,多线程也可能提高程序的吞吐量,假如某个操作被阻塞(同步I/O操作),后续的逻辑不得不等待其完成。如果将其分开,放入不同的线程中,则在I/O上被阻塞的线程不会影响另一个线程被调度执行。比如你等烧水的时间可以刷朋友圈。 这样做的前提是,程序首先是可以被分开,另一个线程的执行并不依赖I/O操作的结果。
- 简化建模:你有三个任务要处理,改BUG,给老板写报告,关注股市行情等。当你正在绞尽脑汁地改bug,老板忽然发消息让你尽快交报告,还同时高频观察股价以便出手解套。有时候你要兼顾几个任务,常常让人倍感疲惫。程序也一样,有很多task要处理,如何以优雅的方式进行是一个设计问题。JAVA允许将复杂应用分解为不同的任务,每个任务分配给独立的线程运行。从而使编程逻辑清晰。很多框架如Servlet,RMI都是利用此模型开发,框架来管理请求,创建线程,平衡负载,分发(dispatch)请求给相应的业务处理组件。屏蔽了底层的细节,开发人员只需要关注业务逻辑。
- 提高用户界面响应:用Eclipse IDE都知道,如果触发一个长时间的任务(编译工程,搜索文件,更新等),界面依然可以接受用户的其他操作。对于长时间的任务,会放入单独的线程里面完成,从而不影响Event Thread处理用户别的很快完成的请求。
- 异步事件处理的简单化: Java NIO提供了一种非阻塞IO机制,在超大量请求来时,可能会触到多线程的性能瓶颈,通过NIO实现单线程内的异步IO,从而减少线程的创建数量,并且不阻塞其他逻辑。但是NIO相对比较复杂也容易出错。而通过简单的多线程,同步IO已经可以满足大部分需求。
线程的风险
- 安全问题:如果你很放心地将这段代码(对象)放入多线程的环境并发调用,那就是二哥。这个getValue方法实现了自增操作,但是此操作不是原子性的(这和数据库的ACID里面的Atomicity原子性是两码事,ACID的原子性不是描述并发的!),此操作包含了读取变量,增加1,写回变量三个操作。
public class IDGenerator{
private int value;
public int getValue() {
return value++;
}
}以下是两个线程交替运行的可能的一种情况。A,B两个线程都读取了初始值9,都增加1,所以变量的结果是10而不是期望的增加两次为11的情况,这种现象也叫丢失更新(lost update)。 如果这个值是被作为数据库的主键的话,那就问题严重了。虽然多线程可以共享内存地址空间,为线程通讯提供了极大便利,但这种读写共享数据的不确定性会带来很大麻烦,我们无法预测运行结果(因为有多种可能性),很难找到问题原因。所以,必须使用JAVA的同步机制来协同多线程串行化访问共享变量。
//使用synchronized 来协同多线程访问,每次调用都会返回唯一值
public class IDGenerator{
private int value;
public synchronized int getNext() {
return value++;
}
}如果不使用同步机制,编译器,运行时,硬件都可以根据需要对代码执行顺序,时间进行优化。比如把变量缓存在CPU寄存器中,并且只对当前线程可见,这种方法优化了程序性能。而在多线程环境下,程序员需要了解如何利用这些优势并且不会破坏安全性是一项挑战。
Liveness 活跃性问题: 死锁,活锁,饥饿问题。
性能问题:设计良好的并发应用程序中,线程能提高程序的整体性能。但是,无论如何,线程总会带来某种程度上的运行时开销。 比如频繁调度时候的上下文切换,保存和恢复(程序计数器等),造成CPU时间更多花在调度而不是执行上。对于共享的数据,同步机制往往迫使编译器放弃某些优化,使缓冲区的数据无效,增加共享内存总线的同步流量。这些因素带来额外性能开销。
线程无处不在,所以安全性无处不在
当你通过java命令启动JVM的那一刻起,线程就已经创建了,JVM会创建守护线程比如垃圾回收器和finalization,main函数的执行则是在的主线程中。许多框架(GUI,Timer, Servlet等)也在管理线程或者线程池,来调用应用程序代码,访问应用程序状态/变量。所以,几乎所有JAVA程序都是多线程的,安全性至关重要。框架通过创建管理线程、线程池回调应用代码,应用代码访问应用数据和状态。所以,别天真以为框架会take care所有安全性相关的问题,保证程序thread-safe,事实上它们做不到. 相反,安全性问题会随着这种调用模式延伸到每一个访问数据的代码路径上。下面举几个简单的例子详细说明。
- Timer: 推迟或者周期运行某个TimerTask任务。应用定义TimerTask,它会在Timer管理的线程中被调用。如果TimerTask访问的数据也被其他线程同时访问,那么,TimerTask和其他线程都需要采用线程安全的方式访问这些数据。 最简单的方法则是,将线程安全封装在被访问对象中。
Servlet/JSP,每一个Servlet封装了一个业务处理逻辑,web程序很可能出现大量相同的请求,所以,这个Servlet将在多个线程中被同时运行,另外,我们有时候需要多个业务逻辑同时访问一些共享的变量如application(存储在ServletContext中)和session(HttpSession存储)范围中的对象(多个Servlet在多个线程中访问应用数据),所以Servelt/jsp,filters,以及这些对象都必须线程安全。
- RM框架,GUI框架都有类似的调用模式和相应的安全性问题。
Java并发简介的更多相关文章
- Java并发—简介与线程创建
程序.进程和线程 程序:一段静态的代码,一组指令的有序集合,不运行的话只是一堆代码. 程序并不能单独执行,只有将程序加载到内存中,系统为他分配资源后才能够执行,这种执行的程序称之为进程.也就是说进程是 ...
- java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四)
Lock接口提供了方法Condition newCondition();用于获取对应锁的条件,可以在这个条件对象上调用监视器方法 可以理解为,原本借助于synchronized关键字以及锁对象,配备了 ...
- java 并发多线程显式锁概念简介 什么是显式锁 多线程下篇(一)
目前对于同步,仅仅介绍了一个关键字synchronized,可以用于保证线程同步的原子性.可见性.有序性 对于synchronized关键字,对于静态方法默认是以该类的class对象作为锁,对于实例方 ...
- 【Java并发.1】简介
继上一本<深入理解Java虚拟机>之后,学习计划里的另一本书<Java并发编程实战>现在开始学习,并记录学习笔记. 第一章主要内容是介绍 并发 的简介.发展.特点. 编写正确的 ...
- 《Java并发编程实战》读书笔记一 -- 简介
<Java并发编程实战>读书笔记一 -- 简介 并发的历史 并发的历史,也是人类利用有限的资源去提高生产效率的一个的例子. 设想现在有台计算机,这台计算机具有以下的资源: 单核CPU一个 ...
- java并发编程笔记(一)——并发编程简介
java并发编程笔记(一)--简介 线程不安全的类示例 public class CountExample1 { // 请求总数 public static int clientTotal = 500 ...
- 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...
- 【Java并发编程实战】-----“J.U.C”:ReentrantLock之一简介
注:由于要介绍ReentrantLock的东西太多了,免得各位客官看累,所以分三篇博客来阐述.本篇博客介绍ReentrantLock基本内容,后两篇博客从源码级别分别阐述ReentrantLock的l ...
- 【Java并发基础】管程简介
前言 在Java 1.5之前,Java语言提供的唯一并发语言就是管程,Java 1.5之后提供的SDK并发包也是以管程为基础的.除了Java之外,C/C++.C#等高级语言也都是支持管程的. 那么什么 ...
随机推荐
- 【C++ Primer】读书笔记_第一章
Main(): 1. C++程序必须包含main()函数,操作系统通过调用main来运行C++程序. 2. main()的形参可以为空. 3. main函数的返回类型必须为int,返回给操作系统.in ...
- vue-cli 3.0 使用axios配置跨域访问豆瓣接口
vue-cli 3.0 配置axios跨域访问豆瓣接口 自己做的小demo 由于豆瓣api跨域问题,因此不能直接通过ajax请求访问,我们通过vue-cli提供给我们的代理 进行配置即可, 在根目录下 ...
- C/C++中的malloc、calloc和realloc
1. malloc 原型:extern void *malloc(unsigned int num_bytes); 头文件:Visual C++6.0中可以用malloc.h或者stdlib.h 功能 ...
- CASE WHEN 批量更新
单个值: UPDATE categories SET display_order = CASE id WHEN 1 THEN 3 WHEN 2 THEN 4 WHEN 3 THEN 5 END WHE ...
- HBase学习(一):认识HBase
一.大数据发展背景 现今是数据飞速膨胀的大数据时代,大数据强调3V特征,即Volume(量级).Varity(种类)和Velocity(速度). ·Volume(量级):TB到ZB. ·Varity( ...
- Linux3.5—IIC学习分析
I2C控制器的设备对象内核已经实现并关联到platform总线. I2C控制器的驱动对象内核已经实现. 看mach-tiny4412.h /plat-samsung/目录下 /drivers/i2c/ ...
- Python在线编程环境
除了安装Python的IDE之外,也可以使用在网页中随时随地编写Python程序. Python官网:https://www.python.org/shell Python123:https://py ...
- linux动态链接库
前言 静态链接库会编译进可执行文件,并被加载到内存,会造成空间浪费 静态链接库对程序的更新.部署.发布带来麻烦.如果静态库更新了,使用它的应用程序都需要重新编译.发布给用户(对于玩家来说,可能是一个很 ...
- Tomcat 8.5 基于 Apache Portable Runtime(APR)库性能优化
Tomcat可以使用Apache Portable Runtime来提供卓越的性能及可扩展性,更好地与本地服务器技术的集成.Apache Portable Runtime是一个高度可移植的库,位于Ap ...
- 20145202马超《网络对抗》Exp7 网络欺诈技术防范
本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法.具体有(1)简单应用SET工具建立冒名网站(2)ettercap DNS spoof(3)结合应用两种技术,用DNS spo ...