Java基础——同步与锁

Java基础——同步与锁

volatile

特色:html

  1. 提供一种轻量级的同步机制,没有线程调度和上下文切换的开销。
  2. 保证变量在多个线程之间的可见性(共享),强制变量的读写操做都在主存上进行,而不是在寄存器或CPU缓存上,使变量的修改能够被全部线程看到。
  3. volatile变量不保证原子性,所以,同步操做仍是须要经过锁机制来实现。

不适用的场景:java

场景一:变量用于非原子的操做编程

private volatile int count;

public void increment() {
    count++;
}

++操做不是原子操做,volatile不能保证数据的原子性。若是须要原子的读写操做,可使用concurrent包里的AtomicXxx类。缓存

场景二:变量与其它变量在一个不变式条件中(以下:lower < upper是一个不变式)多线程

public class NumberRange {
	private int lower, upper;

	public int getLower() { return lower; }
	public int getUpper() { return upper; }

	public void setLower(int value) { 
    	if (value > upper) 
        	throw new IllegalArgumentException(...);
    	lower = value;
	}

	public void setUpper(int value) { 
    	if (value < lower) 
        	throw new IllegalArgumentException(...);
    	upper = value;
	}
}

多线程状况下,若是两个线程同时修改lower和upper,这时候即便是volatile也不能保证lower < upper这个不变式始终成立。并发

场景三:变量须要加锁访问的状况,由于已经用了锁,volatile显然是多余的。jvm

CAS

特色:性能

  1. 一种轻量级的锁,基于硬件CPU指令实现。这是一种乐观锁,假设不会产生冲突而不加锁地进行操做,若是操做失败就不停地重试,直到操做成功。是一种非阻塞的机制。优化

  2. 原理:提供3个操做数,分别是内存位置V,原有的预期值A,要修改的新值B,只有当A=V时,才把V的值修改成B,不然,一直重试,直到成功。.net

    public final int incrementAndGet() {
     	for (;;) {
         	int current = get();
         	int next = current + 1;
         	if (compareAndSet(current, next))
             	return next;
     	}
     }

问题:

  1. ABA的问题:大部分场景下(关注值的最终结果,不关注过程),咱们不须要关心这个问题。
  2. 循环开销问题:若是CAS操做一直失败,会长时间占用cpu资源。

synchronized

特色:

  1. jvm的内置的锁机制,重量级的锁,独占锁,悲观锁,可重入锁。
  2. 做用于代码块,自动释放锁。
  3. 使用简单,jvm自己作了不少优化。(优先仍是要使用这个锁)

ReentrantLock

特色:

  1. 提供与synchronized相同的互斥性和内存可见性。重量级锁,独占锁,悲观锁。
  2. 比synchronized有更灵活的加锁机制。可定时,可轮询,可中断,公平性选择。 ps:在一些并发访问的业务场景下,能够用ReentrantLock来灵活地控制并发操做,其可中断的特色能够很好地控制多线程并发的拥堵问题。
  3. 必须在finally中手动释放锁。

ReentrantReadWriteLock

特色:

  1. 无论是synchronized仍是ReentrantLock,都是强类型的互斥锁,并发状况下的性能会受影响。可是在一些应用场景下,可能读操做要多于写操做,在没有写操做的时候,若是用这种强互斥锁的话,会严重影响读线程的性能。因此能够考虑用ReentrantReadWriteLock来代替上面的强互斥锁,让多个读线程能够并发地访问被保护的对象,提升吞吐量。PS:能够在LinkedHashMap上使用ReentrantReadWriteLock来实现高性能的LRU Cache.

  2. 另一种读写锁的实现是经过volatile(保证读可见) + synchronized(保证写原子性)来实现。

锁优化和使用

  1. 尽可能缩小锁定的范围(好比synchronized,能指定代码块就不要在方法上加锁,尽可能只包含须要受保护的对象的操做)。
  2. 锁分离技术,把须要受保护的对象拆分红多个小对象,每一个小对象都由独立的锁来保护。(能够参考ConcurrentHashMap)
  3. 从性能上来讲:volatile > cas > synchronized,因此在合适的场景下能够选择性能更好的方案。

参考资料:

  1. 《java并发编程实战》
  2. http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

推荐阅读:

Java基础——经常使用Map的实现细节

MySQL使用与优化总结

相关文章
相关标签/搜索