一、synchronized关键字
Lock和Condition接口为程序设计人员提供了高度的锁定控制。然而,大多数情况下,并不需要那样的控制,并且可以使用一种嵌入到Java语言内部的机制。从1.0版开始,Java中的每一个对象都有一个内部锁。如果一个方法用synchronized关键字声明,那么对象的锁将保护整个方法。也就是说,要调用该方法,线程必须获得内部的对象锁。
换句话说:
public synchronized void method(){
method body
}
等价于
public void method(){
this.intrinsicLock.lock();
try{
method body
}
finally{ this.intrinsicLock.unlock();}
}
内部对象锁只有一个相关条件。wait方法添加一个线程到等待集中,notifyAll/notify方法解除等待线程的阻塞状态。换句话说:调用wait或notifyAll等价于intrinsicCondition.await(); intrinsicCondition.signalAll();
注:wait、notifyAll以及notify方法是Object类的final方法。Condition方法必须被命名为await、signalAll和signal以便它们不会与那些方法发生冲突。
修改后的Bank类如下:
public synchronized void transfer(int from, int to, double amount)
throws InterruptedException {
while (accounts[from] < amount)
wait();
accounts[from] -= amount;
accounts[to] += amount;
notifyAll();
}
public synchronized double getTotalBalance() {
double sum = 0;
for (double a : accounts) {
sum += a;
}
return sum;
}
使用synchronized关键字编写代码要简洁的多。当然,必须了解每个对象有一个内部锁,并且该锁有一个内部条件。由锁来管理那些试图进入synchronized方法的线程,有条件来管理那些试图调用wait的线程。
将静态方法声明为synchronized也是合法的。如果调用这种方法,该方法获得相关的类对象的内部锁。如果类对象的内部锁被锁住,没有其他的线程可以调用同一个类的这个或任何其他的同步静态方法。
注:内部锁和条件存在一些局限。包括:
a>不能中断一个正在试图获得锁的线程。
b>试图获得锁时不能设定超时。
c>每个锁仅有单一的条件,可能是不够的。
在代码中应该使用哪一种?Lock和Condition对象还是同步方法?建议如下:
i>最好既不使用Lock/Condition也不使用synchronized关键字。在许多情况下你可以使用java.util.concurrent包中的一种机制,它会为你处理所有的加锁。
ii>如果synchronized关键字适合你的程序,那么请尽量使用它,这样可以减少编写的代码数量,减少出错的几率。
iii>如果特别需要Lock/Condition结构提供的独有特性,才使用Lock/Condition。
常用方法:notifyAll() 、notify() 、wait()
解除或导致线程的阻塞状态,以上方法只能在同步方法或同步代码块中调用(即:使用synchronized关键字的地方)。如果当前线程不是对象锁的持有者,该方法抛出一个IllegalMonitorStateException异常。
修改Bank的代码如下:
package com.thread.bank.syn;
public class Bank {
private final double[] accounts;
public Bank(int n, double initialBalance) {
accounts = new double[n];
for (int i = 0; i < accounts.length; i++) {
accounts[i] = initialBalance;
}
}
public synchronized void transfer(int from, int to, double amount)
throws InterruptedException {
while (accounts[from] < amount)
wait();
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f from %d to %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
notifyAll();
}
public synchronized double getTotalBalance() {
double sum = 0;
for (double a : accounts) {
sum += a;
}
return sum;
}
public int size() {
return accounts.length;
}
}