总结自《Java编程思想》
解决线程冲突问题都是采用序列化访问共享资源方案:在代码前加上一条锁语句,使得在一段时间内只有一个线程可以运行这段代码。因为锁语句产生了一种能够互相排斥的效果,所以这种机制被称为互斥量。
Java提供的synchronized关键字可以进行同步。如果某个任务处于一个对标记为synchronized的方法的调用中,那么在这个线程从该方法返回之前,其他所有要调用类中任何标记为synchronized方法的线程都会被阻塞。所有对象都自动含有单一的锁(也称为监视器),其所有synchronized方法共享同一个锁,即对象本身,this,这可以被用来防止多个任务同时访问。
注意,在使用并发时,将域设置为private是非常重要的,否则,synchronized关键字就不能防止其他任务直接访问域,这样就会产生冲突。
一个任务可以多次获得对象的锁,例如,方法a调用了方法b,方法b又调用了方法c(a、b、c都是同一个对象的方法)。只有首先获得了锁的任务才能允许继续获取多个锁。
每个类的锁是Class实例,所以synchronized static方法可以在类的范围内防止对static数据的并发访问。
每个访问临界共享资源的方法都必须被同步,否则不会正确的工作。
将方法中的部分代码进行同步,这部分代码被称为临界区,也被称为同步代码块。这里,synchronized被用来指定某个对象。采用同步代码块的好处是可以使多个任务访问对象的时间性能得到显著提高。
如果在一个对象中,同步方法和同步代码块锁的不是同一个对象,那么这两个方法可以同时执行,不会阻塞。
除了synchronized,Lock可以进行提供显示的互斥机制。Lock必须被显示的创建、锁定和释放。
public class ReentrantLockTest {
private int count;
private Lock lock = new ReentrantLock();//创建锁
public void next() {
try {
lock.lock();//锁定对象
count++;
} catch (Exception e) {
} finally {
lock.unlock();//必须在这里释放锁,防止出现异常,锁无法释放
}
}
}
Lock获取锁的方式有以下几种:
lock():获取锁,如果锁不可用,当前线程会处于休眠状态,直到获取锁。
tryLock():如果锁可用,获取锁,立即返回true,否则返回false。
tryLock(long time, TimeUnit unit):在指定时间内获取锁,并且线程没有调用interrupt方法。
如果锁可用,获取锁并且立即返回true;
如果锁不可用,会进入休眠状态,直到获取锁(返回true)或者其他线程interrupt当前线程,或者在指定时间内未获取锁(返回false)。
注意:在一个被同步的区域内部,不要调用设计成要被覆盖的方法或者是由客户端以函数对象的形式提供的方法。这样的方法被认为是外来的,无法控制,可能会导致异常、死锁或者数据损坏。
相关文章推荐:
深入JVM锁机制之一:synchronized:http://developer.51cto.com/art/201111/304378.htm
synchronized和Lock的区别:http://houlinyan.iteye.com/blog/1112535