一)并发编程的优势
提高了计算机设备的资源利用率
程序的基本调度单位是线程,在多核处理时代,并发编程大大提高了cpu的使用率
计算多任务比单任务串行程序更加便利
对于一些窗口类程序跟web操控页面,并发编程能可以把操作任务放到后台的独立多个线程里,这种异步操作处理,大大提高了用户界面的响应速度
建模更加简单
在串行变成的年代完成一个复杂任务的情况下,复杂任务分解的几个小任务必须是串行执行,对一类事情进行程序建模是比较困难的。在并发情况下,一个复杂任务分解成的每个小任务可以单独一个线程进行执行,这样针对某类业务的建模会更见简单
并发变成的风险
并发编程是把双刃剑,处理不得当就会遇到以下问题
1.安全问题
线程安全问题是现在java开发人员经常会遇到的问题,尤其是互联网企业里!
例如如下:一个自增的程序在多线程情况下就会出现计数不准问题
publicclassSequence{
privateintvalue;/**返回下一个序列**/
publicintgetNext(){
returnvalue++;
}
}
上面程序是因为在不同线程之间进行交替执行情况下很有可能出现结果与预期的不一样
2.性能问题
并不是多线程就一定能提高程序性能。线程总会带来某种程度上的运行时开销
二)线程安全性
所谓线程安全性的程序主要就是要对访问状态进行控制管理,尤其是对共享与可变状态的访问
共享:意味着多个线程可以同时访问
可变:意味着变量的值在其生命周期内发生变化
所以所谓的线程安全的概念定义:就是在多个线程访问同一个类或者可变变量,进行写入操作的时候,通过同步机制来协同管理对类与变量的访问,能达到多线程访问情况下,某个类或者变量始终能输出正确的结果,那么这个程序就是所谓的线程安全的
线程安全的手段
不在线程间存在共享变量
把共享变量变成final不可修改类型
在访问共享变量情况下,使用同步
前两种手段无需赘述,本博客一系列文章都会围绕第三种手段进行讲解!
一些概念
原子性:
所谓的原子性就是一个操作不能进行再分解。
我们举例说明:还是上个描述了一个返回自增序列的程序:
publicclassSequence{
privateintvalue;
/**返回下一个序列**/
publicintgetNext(){
returnvalue++;
}
}
getNext方法里针对value++操作其实可以分解成三步(获取-修改-写入)
读取value的值
value进行+1操作计算结果
把结果写入value
所以这个value++就不是具有原子性
竟态条件
在多个线程交替执行的时候就会发生竟态条件,还是上面的例子进行说明,value++被分解的三步,其实每一步都依赖于上一步的结果来进行下一步的操作,但是在多线程操作性,执行时序会错乱,这就会导致结果会出现不正确的结果,类似这种的情况就被称为竟态条件
典型的竟态条件就是“先检查后执行”操作,通过一个可能错误的结果来决定下一步的动作。
我们日常编码过程中最经常发生竟态条件的就是那种“懒加载”(延迟加载)的代码程序
/**
懒加载程序示例
**/
publicclassLazyInit{
privateLazyInitinstance=null;
publicstaticgetInstance(){
if(instance==null){
instance=newLazyInit();
}
returninstance;
}
}
例子中假如线程a获取instance停在了8行,正要执行第9行的时候,交出了执行权,线程b进来一路执行,也判断instance为null,new了一个实例并且进行了返回。然后线程a获得执行权,也new了一个实例要进行了返回。两个线程获取的实例不是同一个对象。这显然也是错误的。
复合操作与加锁机制
之前例子的value++要想获得正确的结果,必须通过同步手段把实际上的三步变成原子一起执行。把这类多步骤变成原子性的操作成为复合操作,而同步手段称作加锁机制
java提供了内置的锁机制来支持原子性:同步代码块(synchroinzed)jdk1.5Concurrent包提供了Lock也可以来支持原子性操作
文章来源于网络,版权归原创者所有!如有侵权,请及时联系删除!赞赏