天天看点

Java 多线程下race condition/同步/原子操作问题

对于同步,除了同步方法外,还可以使用同步代码块,有时候同步代码块会带来比同步方法更好的效果。

追其同步的根本的目的,是控制竞争资源的正确的访问,因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问即可,因此Java引入了同步代码快的策略,以提高性能

Java多线程的同步依靠的是对象锁机制,synchronized关键字的背后就是利用了封锁来实现对共享资源的互斥访问。

要想实现线程的同步,则这些线程必须去竞争一个唯一的共享的对象锁。

1.  Hashmap不是读写线程安全的,只有全部只读才是线程安全的,Hashmap在被并发读写使用的时候会出现线程安全问题,一般理解的线程安全问题导致的是数据错误。 而Hashmap多线程同时读写操作时,可能使程序挂起。在高并发的情况下,HashMap的桶在存了大量的数据后get操作的for循环取对象的操作在同时有读有写的情况下变得不可预知。服务器的CPU可能会暴涨, 而且一直下不去.

解决方法:  如果知道要在多线程情况下读写Map, 建议使用线程安全的ConcurrentHashMap实现代替HashMap。ConcurrentHashMap 可以在不损失线程安全的同时提供很好的并发性。

2. ArrayList是线程不安全的,多个线程访问同一个ArrayList集合时,如果有超过一条线程修改了ArrayList集合,则程序必须手动保证该集合的同步性。

解决方法:  ArrayList和Vector作为List接口的两个典型实现, Vector集合则是线程安全的, 所以Vector的性能比ArrayList的性能要低.

1:什么是原子操作? 

线程执行的最小单位,不能中断。

2:volatile原理是什么? 

一般变量,在主存和当线程的临时内存中都一份缓存/副本,为保证数据

 统一,需要同步,当同步没有完成时,如果其他线程读取主存上的值就会导致不同步异常。

Volatile就是告诉处理器这个变量是不稳定的,不要在线程的临时内存缓存该数据,直接使用JVM内存的数据,就避免了同步的步骤。

3:对volatile变量数据进行修改(不是重赋值),是否能保证同步?

不能。即使是++,--也不是原子操作,会因为CPU的线程的切换,导致数据不可控。 

4:那为什么还要用volatile这种修饰符? 

对volatile变量读取,和赋值是可以保证原子性

5:那到底如何解决这样的问题? 

        第一种:采用同步synchronized解决,这样虽然解决了问题,但是也降低了系统的性能。 

        第二种:采用原子性数据Atomic变量,这是从JDK1.5开始才存在的针对原子性的解决方案,这种方案也是目前比较好的解决方案了。

6:Atomic的实现基本原理? 

 * 1)Atomic中的变量是申明为了volatile变量的,这样就保证的变量的存储和读取

 * 是一致的,都是来自同一个内存块,

 * 2)Atomic对变量的++操作进行了封装,提供了getAndIncrement方法,

 * ,并提供了compareAndSet方法,来完成对单个变量的加锁和解锁操作,

 * Atomic虽然解决了同步的问题,但是性能上面还是会有所损失,不过影响不大。

7:volatile 是否可以修饰对象实例?为什么对象变量重新赋值也是原子操作?

可以。JVM保证volatile的读操作一定发生在写操作之后,即使没有使用sycnrhonized语块。

因为在栈中,对象变量存储的是一个地址,该地址是指向堆中对象的,所以操作并不负责,

可以认为对一个对象重新赋值是原子操作。

8:为什么在Java中变量赋值中,除了long和double类型的变量外都是原子操作?

由于long和double类型是大于32BIT的,JVM把他们作为2个原子性的32位值来对待,需要进行2次赋值。所以,不是原子操作。 

如果,硬件和软件都支持64BIT,则long和double可能就是原子操作。

另外,对象引用使用本机指针实现,通常也是32位的。对这些32位的类型的操作是原子的。

http://www.cnblogs.com/redcreen/archive/2011/03/29/1999032.html

http://www.cnblogs.com/zhxxcq/archive/2012/04/18/2455916.htmlj

http://www.knowsky.com/369575.html

synchronized 关键字 http://baike.baidu.com/view/1207212.htm