您的位置:首页 > 编程语言 > Java开发

Java多线程和并发编程实践的学习心得----基础篇3

2014-10-26 11:17 274 查看

Java多线程和并发编程实践的学习心得----基础篇3

三 线程的同步与并发

并发:并发就是多个线程同时去完成一点复杂的事情,在实际应用中,经常用到线程的并发,多条线程做完成一件事情和一条线程去完成一件事情,那是无法同言而语的。 同时并发编程,可以充分利用服务器资源,提高服务吞吐量、降低响应时间(爬虫、WebServer、日志分析……),以及在一些分布式系统(资源的争用、可伸缩性)的服务器编程中到处都要用的并发编程。

同步:当线程并发的时候,随之而来的也带来了一些问题,如果多条线程去同时操作共享而用的数据那将是会使共享的数据很容易就出现错误。在JAVA中提供同防止多条线程同时共享数据的方式是:synchronized,volatile很好的利用这两个关键字就能防止并发而带来的问题, 同时在JAVA1.5之后也提供了更多好用的类来解决这个问题 。

线程池:当有许多请求需要去处理的时候,如果只是单独的一个人去处理,可想而知那会让后面在排队的人等多久,这样就需要线程池,有请求过来了就到线程池里 面取出一条线程去处理它,处理完成就把它收回到线程池里面,然而自己实现 一个功能强大的线程池也并非易事,在java1.5之后专门提供了线程池的类库。下图是线程池的模型:



1 常见术语解释:

可见性:在没有使用同步机制的情况下,一个线程对某个共享对象的修改,并不会立即被其它的线程读取到。

在Java内存抽象模型中,如果多个线程对一个变量进行操作,但是这多个线程有可能被分配到多个处理器中运行,那么编译器会对代码进行优化,当线程要处理该变量时,处理器会将变量从主内存复制一份分别存储在自己的片上存储器中,等到进行完操作后,再赋值回主存。这样做的好处是提高了运行的速度;

上上一节,基础篇1中已经看到过这个图了,这个是Java虚拟机内存模型的抽象模型:



所以,从上图可以看出,如果线程A与线程B分别被安排在了不同的处理器上面,那么t1与t2对于变量A的修改时相互不可见,如果A给x赋值,然后B又赋新值,那么B的操作就将A的操作覆盖掉了,这样会产生不可预料的结果。

同时,在有共享变量的多线程程序中,也会发生问题,这里就涉及到共享对象的可见性了,也就是在没有使用同步机制的情况下,一个线程对某个共享对象的修改,并不会立即被其它的线程读取到。

JMM中的happened-before规则:

如果线程A写入了volatile变量v,接着线程B读取了v,那么,线程A写入v及之前的写操作都对线程B可见。
线程中上一个动作及之前的所有写操作在该线程执行下一个动作时对该线程可见
如果线程A解锁了,接着线程B锁定了a,那么,线程A解锁a之前的写操作都对线程2可见
happends-before有传递性

原子性:原子的意思代表着——“不可分”

顾名思义:原子性就是不被线程调度器中断的操作。
如赋值或者return。比如"a = 1;"和 "return a;"这样的操作都具有原子性

但是类似"a += b"后者 “return i++ ”这样的操作不具有原子性。

在JVM中"a += b"可能要经过这样三个步骤:(1)取出a和b (2)计算a+b (3)将计算结果写入内存
如果有两个线程t1,t2在进行这样的操作。t1在第二步做完之后还没来得及把数据写回内存就被线程调度器中断了,于是t2开始执行,t2执行完毕后t1又把没有完成的第三步做完。这个时候就出现了错误,相当于t2的计算结果被无视掉了。 等等,这些非原子性操作在多线程的运行程序中非常的多见。 所以,在多线程的编程中,一定要注意同步加锁等操作。

有序性:程序执行顺序

首先要明白,代码顺序并不一定等于,程序执行的顺序。

如在单线程的例子中:一般情况下,代码顺序等于程序执行的顺序。但如果没有数据依赖的话是允许重排序,就是不一定安装代码的顺序执行,由于没有数据依赖,对程序的结果不会产生任何影响。

如在多线程中可就不一样了,多线程中3个显著特点是:

整体无序:i++
线程内重排序
线程间看到的顺序也不一致(可见性导致)

看看理想的情况下:Java虚拟机中的顺序一致性。



但是在真实情况是这样的,在正确同步的前期下:

[b]

[/b]



[b]线程安全:


[/b]


线程安全就是多线程访问时,采用了加锁等机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据


或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也

就是说我们不用考虑同步的问题。


线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个
线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

2 锁的原理



任何对象都有一个monitor(监视器/管程)与之关联,当且一个monitor被持有后,它将处于锁定状态。Java中

就是synchronized 和volatile关键字了。下面来比较下他们的区别:

•synchronized可以做到:–可重入、互斥性、可见性

•synchronized不能做到:–等待超时、可中断、公平性

•volatile可以做到:–原子性、可见性

•volatile不能做到:–复合操作的原子性 –volatile引用指向对象属性/数组元素的可见性

•volatile提供:–与锁一致的语义,并且比锁的开销小

下一节看看java在java.util.concurrent.*类中的关于对线程的知识点:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: