Volatile与sychronized

    在使用 volatile 关键字时要慎 重,并不是只要简单类型变量使用 volatile 修饰,对这个变量的所有操作都是原来操作,当变量的值由自身的上一个决定时,如 n=n+1 n++ 等, volatile 关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如 n = m + 1 ,这个就是原级别的。所以在使用 volatile 关键时一定要谨慎,如果自己没有把握,可以使用 synchronized 来代替 volatile

Volatile: 用在多线程,同步变量。 线程为了提高效率,将某成员变量 ( A) 拷贝了一份(如 B ),线程中对 A 的访问其实访问的是 B 。只在某些动作时才进行 A B 的同步。因此存在 A B 不一致的情况。 volatile 就是用来避免这种情况的。 volatile 告诉 jvm 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的 A)

Synchronized: 同步方法或变量的同时也锁定了类

 

 

深入了解 synchronized 及对象锁

2008-09-09 10:361 , Synchronized 锁定的是对象而非函数或代码。

2 , 每个 Object 只有一把锁 (Lock) 与之关联,当进行到 Synchronized 语句或函数的时候,这把锁就会被当前的线程( thread )拿走,其他的( thread )再去访问的时候拿不到锁就被暂停了

3, 只有当 Synchronized 的是同一个对象的才是线程安全的( thread-safe

:

    1) public Synchronized void method1 : 锁住的是该对象 , 类的其中一个实例 , 当该对象 ( 仅仅是这一个对象 ) 在不同线程中执行这个同步方法时 , 线程之间会形成互斥 . 达到同步效果 , 但如果不同线程同时对该类的不同对象执行这个同步方法时 , 则线程之间不会形成互斥 , 因为他们拥有的是不同的锁 .

    2) Synchronized(this){ //TODO } 同一

 

    3) public Synchronizedstatic void method3 : 锁住的是该类 , 当所有该类的对象 ( 多个对象 ) 在不同线程中调用这个 static 同步方法时 , 线程之间会形成互斥 , 达到同步效果 , 但如果多个线程同时调用 method1 , method3, 则不会引互斥 , 具体讲看最后讲解 .

    4) Synchronized(Test.class){ //TODo} 同三

 

    5) synchronized(o) {} 这里面的 o 可以是一个任何 Object 对象或数组 , 并不一定是它本身对象或者类 , 谁拥有 o 这个锁 , 谁就能够操作该块程序代码 .

 

这里面 1) 2) 是线程安全的 , 1) 3) , 2) 3) 他们所拥有的锁不一样 , 故不是同步的 , 不是线程安全的 .

 

 

最后 , synchronized 所同步的代码应该尽量少 . 如果没有必要同步的就不要列为其中

     联想到数据库的并发控制

假如有如下操作

    1) 从数据库中读取数据

    2) 修改数据

    3) 提交数据

解决方案一

1) 读取数据

synchronized(XXX)

{

    2) 修改数据

    3) 提交数据

解决方案二

synchronized(XXX)

{

    1) 读取数据

    2) 修改数据

    3) 提交数据

}

 

解决方案三

synchronized(XXX)

{

     3) 提交数据

}

 

这里面到底用哪一种方案视具体情况而定 , 要注意的是 synchronized( 同步 ) 的代价 , 开销是很大的 .

 

 

 

volatile 关键字有什么用?

  恐怕比较一下 volatile synchronized 的不同是最容易解释清楚的。 volatile 是变量修饰符,而 synchronized 则作用于一段代码或方法;看如下三句 get 代码:

int i1;                          int geti1() {return i1;}

volatile int i2;              int geti2() {return i2;}

int i3; synchronized     int geti3() {return i3;}

 

   geti1() 得到存储在当前线程中 i1 的数值。多个线程有多个 i1 变量拷贝,而且这些 i1 之间可以互不相同。换句话说,另一个线程可能已经改变了它线程内的 i1 值,而这个值可以和当前线程中的 i1 值不相同。事实上, Java 有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的 i1 值是 1 ,线程 1 里的 i1 值是 2 ,线程 2 里的 i1 值是 3 ——这在线程 1 和线程 2 都改变了它们各自的 i1 值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。

  而 geti2() 得到的是“主”内存区域的 i2 数值。用 volatile 修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经 volatile 修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的, volatile 修饰的变量存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。

  既然 volatile 关键字已经实现了线程间数据同步,又要 synchronized 干什么呢?呵呵,它们之间有两点不同。首先, synchronized 获得并释放监视器——如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事实。但是, synchronized 也同步内存:事实上, synchronized 在“主”内存区域同步整个线程的内存。因此,执行 geti3() 方法做了如下几步:

1. 线程请求获得监视 this 对象的对象锁(假设未被锁,否则线程等待直到锁释放)

2. 线程内存的数据被消除,从“主”内存区域中读入( Java 虚拟机能优化此步。。。 [ 后面的不知道怎么表达 , ]

3. 代码块被执行

4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过 geti3() 方法不会改变变量值)

5. 线程释放监视 this 对象的对象锁

  因此 volatile 只是在线程内存和“主”内存间同步某个变量的值,而 synchronized 通过锁定和解锁某个监视器同步所有变量的值。显然 synchronized 要比 volatile 消耗更多资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值